Browse Source

Merge branch 'master' into deploy-ng

rdb 7 years ago
parent
commit
f535a641e4
100 changed files with 5752 additions and 5912 deletions
  1. 5 0
      README.md
  2. 51 0
      contrib/src/panda3dtoolsgui/build_exe.bat
  3. 40 0
      contrib/src/rplight/config_rplight.h
  4. 185 0
      contrib/src/rplight/gpuCommand.I
  5. 87 0
      contrib/src/rplight/gpuCommand.cxx
  6. 89 0
      contrib/src/rplight/gpuCommand.h
  7. 82 0
      contrib/src/rplight/gpuCommandList.cxx
  8. 54 0
      contrib/src/rplight/gpuCommandList.h
  9. 233 0
      contrib/src/rplight/iesDataset.cxx
  10. 68 0
      contrib/src/rplight/iesDataset.h
  11. 140 0
      contrib/src/rplight/internalLightManager.I
  12. 441 0
      contrib/src/rplight/internalLightManager.cxx
  13. 102 0
      contrib/src/rplight/internalLightManager.h
  14. 13 0
      contrib/src/rplight/p3rplight_composite1.cxx
  15. 239 0
      contrib/src/rplight/pointerSlotStorage.h
  16. 243 0
      contrib/src/rplight/pssmCameraRig.I
  17. 396 0
      contrib/src/rplight/pssmCameraRig.cxx
  18. 125 0
      contrib/src/rplight/pssmCameraRig.h
  19. 406 0
      contrib/src/rplight/rpLight.I
  20. 137 0
      contrib/src/rplight/rpLight.cxx
  21. 130 0
      contrib/src/rplight/rpLight.h
  22. 82 0
      contrib/src/rplight/rpPointLight.I
  23. 90 0
      contrib/src/rplight/rpPointLight.cxx
  24. 63 0
      contrib/src/rplight/rpPointLight.h
  25. 74 0
      contrib/src/rplight/rpSpotLight.I
  26. 80 0
      contrib/src/rplight/rpSpotLight.cxx
  27. 71 0
      contrib/src/rplight/rpSpotLight.h
  28. 159 0
      contrib/src/rplight/shadowAtlas.I
  29. 186 0
      contrib/src/rplight/shadowAtlas.cxx
  30. 80 0
      contrib/src/rplight/shadowAtlas.h
  31. 192 0
      contrib/src/rplight/shadowManager.I
  32. 157 0
      contrib/src/rplight/shadowManager.cxx
  33. 91 0
      contrib/src/rplight/shadowManager.h
  34. 262 0
      contrib/src/rplight/shadowSource.I
  35. 41 0
      contrib/src/rplight/shadowSource.cxx
  36. 93 0
      contrib/src/rplight/shadowSource.h
  37. 93 0
      contrib/src/rplight/tagStateManager.I
  38. 202 0
      contrib/src/rplight/tagStateManager.cxx
  39. 93 0
      contrib/src/rplight/tagStateManager.h
  40. 4 4
      contrib/src/sceneeditor/seFileSaver.py
  41. 1 8
      contrib/src/sceneeditor/seLights.py
  42. 2 2
      direct/src/actor/Actor.py
  43. 6 85
      direct/src/dcparser/dcClass.cxx
  44. 0 5
      direct/src/dcparser/dcClass.h
  45. 2 2
      direct/src/dcparser/dcField.cxx
  46. 6 9
      direct/src/dcparser/dcmsgtypes.h
  47. 2 2
      direct/src/directtools/DirectSelection.py
  48. 2 0
      direct/src/distributed/CRDataCache.py
  49. 3 3
      direct/src/distributed/ClientRepositoryBase.py
  50. 1 0
      direct/src/distributed/ConnectionRepository.py
  51. 2 0
      direct/src/distributed/DistributedNode.py
  52. 0 1
      direct/src/distributed/DistributedNodeUD.py
  53. 7 15
      direct/src/distributed/DistributedObjectAI.py
  54. 8 1
      direct/src/distributed/DistributedObjectBase.py
  55. 2 2
      direct/src/distributed/DistributedObjectUD.py
  56. 13 9
      direct/src/distributed/DoInterestManager.py
  57. 133 97
      direct/src/distributed/MsgTypes.py
  58. 1 1
      direct/src/distributed/MsgTypesCMU.py
  59. 0 208
      direct/src/distributed/OldClientRepository.py
  60. 5 8
      direct/src/distributed/PyDatagram.py
  61. 9 9
      direct/src/distributed/cConnectionRepository.cxx
  62. 2 2
      direct/src/distributed/cDistributedSmoothNodeBase.cxx
  63. 7 1
      direct/src/filter/CommonFilters.py
  64. 5 0
      direct/src/gui/DirectEntry.py
  65. 22 23
      direct/src/gui/DirectGuiBase.py
  66. 2 2
      direct/src/plugin_installer/FileAssociation.nsh
  67. 27 16
      direct/src/showbase/ShowBase.py
  68. 29 9
      direct/src/showbase/ShowBaseGlobal.py
  69. 74 2
      dmodels/src/gui/radio_button_gui.egg
  70. 0 33
      dtool/src/attach/ctallihave
  71. 0 150
      dtool/src/attach/ctattach.drv
  72. 0 86
      dtool/src/attach/ctattachcc
  73. 0 530
      dtool/src/attach/ctattch.pl
  74. 0 954
      dtool/src/attach/ctattch.pl.rnd
  75. 0 430
      dtool/src/attach/ctccase.pl
  76. 0 70
      dtool/src/attach/ctci
  77. 0 579
      dtool/src/attach/ctcm.pl
  78. 0 69
      dtool/src/attach/ctco
  79. 0 451
      dtool/src/attach/ctcvs.pl
  80. 0 57
      dtool/src/attach/ctdelta
  81. 0 232
      dtool/src/attach/ctdelta.pl
  82. 0 232
      dtool/src/attach/ctdelta.pl.rnd
  83. 0 33
      dtool/src/attach/ctihave
  84. 0 23
      dtool/src/attach/ctmake
  85. 0 55
      dtool/src/attach/ctmkdir
  86. 0 78
      dtool/src/attach/ctmkelem
  87. 0 35
      dtool/src/attach/ctmv
  88. 0 258
      dtool/src/attach/ctntool.pl
  89. 0 60
      dtool/src/attach/ctproj.pl
  90. 0 42
      dtool/src/attach/ctquery
  91. 0 37
      dtool/src/attach/ctquery.pl
  92. 0 31
      dtool/src/attach/ctrm
  93. 0 43
      dtool/src/attach/ctsanity
  94. 0 43
      dtool/src/attach/cttimewarp
  95. 0 49
      dtool/src/attach/ctunattach.drv
  96. 0 251
      dtool/src/attach/ctunattach.pl
  97. 0 31
      dtool/src/attach/ctunco
  98. 0 47
      dtool/src/attach/ctutils.pl
  99. 0 360
      dtool/src/attach/ctvspec.pl
  100. 0 37
      dtool/src/attach/dtool.alias

+ 5 - 0
README.md

@@ -28,6 +28,8 @@ into an existing Python installation is using the following command:
 pip install --pre --extra-index-url https://archive.panda3d.org/ panda3d
 pip install --pre --extra-index-url https://archive.panda3d.org/ panda3d
 ```
 ```
 
 
+If this command fails, please make sure your version of pip is up-to-date.
+
 If you prefer to install the full SDK with all tools, the latest development
 If you prefer to install the full SDK with all tools, the latest development
 builds can be obtained from this page:
 builds can be obtained from this page:
 
 
@@ -51,6 +53,9 @@ depending on whether you are on a 32-bit or 64-bit system:
 https://www.panda3d.org/download/panda3d-1.9.4/panda3d-1.9.4-tools-win32.zip
 https://www.panda3d.org/download/panda3d-1.9.4/panda3d-1.9.4-tools-win32.zip
 https://www.panda3d.org/download/panda3d-1.9.4/panda3d-1.9.4-tools-win64.zip
 https://www.panda3d.org/download/panda3d-1.9.4/panda3d-1.9.4-tools-win64.zip
 
 
+(It is also possible to build using MSVC 2015 and 2017, which requires a
+different set of thirdparty libraries, but that is not described here.)
+
 After acquiring these dependencies, you may simply build Panda3D from the
 After acquiring these dependencies, you may simply build Panda3D from the
 command prompt using the following command:
 command prompt using the following command:
 
 

+ 51 - 0
contrib/src/panda3dtoolsgui/build_exe.bat

@@ -0,0 +1,51 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#include "config_rplight.h"
+
+#include "rpLight.h"
+#include "rpPointLight.h"
+
+#include "dconfig.h"
+
+Configure(config_rplight);
+NotifyCategoryDef(rplight, "");
+
+ConfigureFn(config_rplight) {
+  init_librplight();
+}
+
+void
+init_librplight() {
+  static bool initialized = false;
+  if (initialized) {
+    return;
+  }
+  initialized = true;
+
+  // RPLight::init_type();
+  // RPPointLight::init_type();
+}

+ 40 - 0
contrib/src/rplight/config_rplight.h

@@ -0,0 +1,40 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef CONFIG_RPLIGHT_H
+#define CONFIG_RPLIGHT_H
+
+#include "pandabase.h"
+#include "notifyCategoryProxy.h"
+#include "configVariableDouble.h"
+#include "configVariableString.h"
+#include "configVariableInt.h"
+
+NotifyCategoryDecl(rplight, EXPORT_CLASS, EXPORT_TEMPL);
+
+extern void init_librplight();
+
+#endif // CONFIG_RPLIGHT_H

+ 185 - 0
contrib/src/rplight/gpuCommand.I

@@ -0,0 +1,185 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#include "stdint.h"
+
+/**
+ * @brief Appends an integer to the GPUCommand.
+ * @details This adds an integer to the back of the GPUCommand. Depending on the
+ *   setting in convert_int_to_float, this will either just convert the int to a
+ *   float by casting it, or just do a bitwise copy.
+ *
+ * @param v The integer to append.
+ */
+inline void GPUCommand::push_int(int v) {
+  push_float(convert_int_to_float(v));
+}
+
+/**
+ * @brief Internal method to convert an integer to float
+ * @details This methods gets called by the GPUCommand::push_int, and manages
+ *   storing an integer in a floating point variable. There are two options,
+ *   which are documented inside of the method.
+ *
+ * @param v Integer to convert
+ * @return Float-representation of that integer, either casted or binary converted.s
+ */
+inline float GPUCommand::convert_int_to_float(int v) const {
+
+  #if !PACK_INT_AS_FLOAT
+    // Just round to float, can cause rounding issues tho
+    return (float)v;
+
+  #else
+    assert(sizeof(float) == 4); // We really need this for packing! Better
+                  // throw an error if the compiler uses more
+                  // than 4 bytes.
+    // Simple binary conversion, assuming sizeof(int) == sizeof(float)
+    union { int32_t _int; float _float; } converter = { (int32_t)v };
+    return converter._float;
+  #endif
+}
+
+/**
+ * @brief Appends a float to the GPUCommand.
+ * @details This adds an integer to the back of the GPUCommand. Its used by all
+ *   other push_xxx methods, and simply stores the value, then increments the write
+ *   pointer. When the amount of floats exceeds the capacity of the GPUCommand,
+ *   an error will be printed, and the method returns without doing anything else.
+ *
+ * @param v The float to append.
+ */
+inline void GPUCommand::push_float(float v) {
+  if (_current_index >= GPU_COMMAND_ENTRIES) {
+    gpucommand_cat.error() << "Out of bounds! Exceeded command size of " << GPU_COMMAND_ENTRIES << endl;
+    return;
+  }
+  _data[_current_index++] = v;
+}
+
+/**
+ * @brief Appends a 3-component floating point vector to the GPUCommand.
+ * @details This appends a 3-component floating point vector to the command.
+ *   It basically just calls push_float() for every component, in the order
+ *   x, y, z, which causes the vector to occupy the space of 3 floats.
+ *
+ * @param v Int-Vector to append.
+ */
+inline void GPUCommand::push_vec3(const LVecBase3 &v) {
+  push_float(v.get_x());
+  push_float(v.get_y());
+  push_float(v.get_z());
+}
+
+
+/**
+ * @brief Appends a 3-component integer vector to the GPUCommand.
+ * @details This appends a 3-component integer vector to the command.
+ *   It basically just calls push_int() for every component, in the order
+ *   x, y, z, which causes the vector to occupy the space of 3 floats.
+ *
+ * @param v Int-Vector to append.
+ */
+inline void GPUCommand::push_vec3(const LVecBase3i &v) {
+  push_int(v.get_x());
+  push_int(v.get_y());
+  push_int(v.get_z());
+}
+
+/**
+ * @brief Appends a 4-component floating point vector to the GPUCommand.
+ * @details This appends a 4-component floating point vector to the command.
+ *   It basically just calls push_float() for every component, in the order
+ *   x, y, z, which causes the vector to occupy the space of 3 floats.
+ *
+ * @param v Int-Vector to append.
+ */
+inline void GPUCommand::push_vec4(const LVecBase4 &v) {
+  push_float(v.get_x());
+  push_float(v.get_y());
+  push_float(v.get_z());
+  push_float(v.get_w());
+}
+
+/**
+ * @brief Appends a 4-component integer vector to the GPUCommand.
+ * @details This appends a 4-component integer vector to the command.
+ *   It basically just calls push_int() for every component, in the order
+ *   x, y, z, w, which causes the vector to occupy the space of 4 floats.
+ *
+ * @param v Int-Vector to append.
+ */
+inline void GPUCommand::push_vec4(const LVecBase4i &v) {
+  push_int(v.get_x());
+  push_int(v.get_y());
+  push_int(v.get_z());
+  push_int(v.get_w());
+}
+
+/**
+ * @brief Appends a floating point 3x3 matrix to the GPUCommand.
+ * @details This appends a floating point 3x3 matrix to the GPUCommand, by
+ *   pushing all components in row-order to the command. This occupies a space of
+ *   9 floats.
+ *
+ * @param v Matrix to append
+ */
+inline void GPUCommand::push_mat3(const LMatrix3 &v) {
+  for (size_t i = 0; i < 3; ++i) {
+    for (size_t j = 0; j < 3; ++j) {
+      push_float(v.get_cell(i, j));
+    }
+  }
+}
+
+/**
+ * @brief Appends a floating point 4x4 matrix to the GPUCommand.
+ * @details This appends a floating point 4x4 matrix to the GPUCommand, by
+ *   pushing all components in row-order to the command. This occupies a space of
+ *   16 floats.
+ *
+ * @param v Matrix to append
+ */
+inline void GPUCommand::push_mat4(const LMatrix4 &v) {
+  for (size_t i = 0; i < 4; ++i) {
+    for (size_t j = 0; j < 4; ++j) {
+      push_float(v.get_cell(i, j));
+    }
+  }
+}
+
+/**
+ * @brief Returns whether integers are packed as floats.
+ * @details This returns how integer are packed into the data stream. If the
+ *   returned value is true, then integers are packed using their binary
+ *   representation converted to floating point format. If the returned value
+ *   is false, then integers are packed by simply casting them to float,
+ *   e.g. val = (float)i;
+ * @return The integer representation flag
+ */
+inline bool GPUCommand::get_uses_integer_packing() {
+  return PACK_INT_AS_FLOAT;
+}

+ 87 - 0
contrib/src/rplight/gpuCommand.cxx

@@ -0,0 +1,87 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#include "gpuCommand.h"
+
+#include <iostream>
+#include <iomanip>
+#include <stdlib.h>
+
+
+NotifyCategoryDef(gpucommand, "");
+
+/**
+ * @brief Constructs a new GPUCommand with the given command type.
+ * @details This will construct a new GPUCommand of the given command type.
+ *   The command type should be of GPUCommand::CommandType, and determines
+ *   what data the GPUCommand contains, and how it will be handled.
+ *
+ * @param command_type The type of the GPUCommand
+ */
+GPUCommand::GPUCommand(CommandType command_type) {
+  _command_type = command_type;
+  _current_index = 0;
+  memset(_data, 0x0, sizeof(float) * GPU_COMMAND_ENTRIES);
+
+  // Store the command type as the first entry
+  push_int(command_type);
+}
+
+/**
+ * @brief Prints out the GPUCommand to the console
+ * @details This method prints the type, size, and data of the GPUCommand to the
+ *   console. This helps for debugging the contents of the GPUCommand. Keep
+ *   in mind that integers might be shown in their binary float representation,
+ *   depending on the setting in the GPUCommand::convert_int_to_float method.
+ */
+void GPUCommand::write(ostream &out) const {
+  out << "GPUCommand(type=" << _command_type << ", size=" << _current_index << ", data = {" << endl;
+  for (size_t k = 0; k < GPU_COMMAND_ENTRIES; ++k) {
+    out << std::setw(12) << std::fixed << std::setprecision(5) << _data[k] << " ";
+    if (k % 6 == 5 || k == GPU_COMMAND_ENTRIES - 1) out << endl;
+  }
+  out << "})" << endl;
+}
+
+/**
+ * @brief Writes the GPU command to a given target.
+ * @details This method writes all the data of the GPU command to a given target.
+ *   The target should be a pointer to memory being big enough to hold the
+ *   data. Presumably #dest will be a handle to texture memory.
+ *   The command_index controls the offset where the data will be written
+ *   to.
+ *
+ * @param dest Handle to the memory to write the command to
+ * @param command_index Offset to write the command to. The command will write
+ *   its data to command_index * GPU_COMMAND_ENTRIES. When writing
+ *   the GPUCommand in a GPUCommandList, the command_index will
+ *   most likely be the index of the command in the list.
+ */
+void GPUCommand::write_to(const PTA_uchar &dest, size_t command_index) {
+  size_t command_size = GPU_COMMAND_ENTRIES * sizeof(float);
+  size_t offset = command_index * command_size;
+  memcpy(dest.p() + offset, &_data, command_size);
+}

+ 89 - 0
contrib/src/rplight/gpuCommand.h

@@ -0,0 +1,89 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef GPUCOMMAND_H
+#define GPUCOMMAND_H
+
+#include "pandabase.h"
+#include "luse.h"
+
+NotifyCategoryDecl(gpucommand, EXPORT_CLASS, EXPORT_TEMPL);
+
+#define GPU_COMMAND_ENTRIES 32
+
+// Packs integers by storing their binary representation in floats
+// This only works if the command and light buffer is 32bit floating point.
+#define PACK_INT_AS_FLOAT 0
+
+/**
+ * @brief Class for storing data to be transferred to the GPU.
+ * @details This class can be seen like a packet, to be transferred to the GPU.
+ *   It has a command type, which tells the GPU what to do once it recieved this
+ *   "packet". It stores a limited amount of floating point components.
+ */
+class GPUCommand {
+PUBLISHED:
+  /**
+   * The different types of GPUCommands. Each type has a special case in
+   * the command queue processor. When adding new types, those need to
+   * be handled in the command target, too.
+   */
+  enum CommandType {
+    CMD_invalid = 0,
+    CMD_store_light = 1,
+    CMD_remove_light = 2,
+    CMD_store_source = 3,
+    CMD_remove_sources = 4,
+  };
+
+  GPUCommand(CommandType command_type);
+
+  inline void push_int(int v);
+  inline void push_float(float v);
+  inline void push_vec3(const LVecBase3 &v);
+  inline void push_vec3(const LVecBase3i &v);
+  inline void push_vec4(const LVecBase4 &v);
+  inline void push_vec4(const LVecBase4i &v);
+  inline void push_mat3(const LMatrix3 &v);
+  inline void push_mat4(const LMatrix4 &v);
+
+  inline static bool get_uses_integer_packing();
+
+  void write_to(const PTA_uchar &dest, size_t command_index);
+  void write(ostream &out) const;
+
+private:
+
+  inline float convert_int_to_float(int v) const;
+
+  CommandType _command_type;
+  size_t _current_index;
+  float _data[GPU_COMMAND_ENTRIES];
+};
+
+#include "gpuCommand.I"
+
+#endif // GPUCOMMAND_H

+ 82 - 0
contrib/src/rplight/gpuCommandList.cxx

@@ -0,0 +1,82 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#include "gpuCommandList.h"
+
+
+/**
+ * @brief Constructs a new GPUCommandList
+ * @details This constructs a new GPUCommandList. By default, there are no commands
+ *   in the list.
+ */
+GPUCommandList::GPUCommandList() {
+}
+
+/**
+ * @brief Pushes a GPUCommand to the command list.
+ * @details This adds a new GPUCommand to the list of commands to be processed.
+ *
+ * @param cmd The command to add
+ */
+void GPUCommandList::add_command(const GPUCommand& cmd) {
+  _commands.push(cmd);
+}
+
+/**
+ * @brief Returns the number of commands in this list.
+ * @details This returns the amount of commands which are currently stored in this
+ *   list, and are waiting to get processed.
+ * @return Amount of commands
+ */
+size_t GPUCommandList::get_num_commands() {
+  return _commands.size();
+}
+
+/**
+ * @brief Writes the first n-commands to a destination.
+ * @details This takes the first #limit commands, and writes them to the
+ *   destination using GPUCommand::write_to. See GPUCommand::write_to for
+ *   further information about #dest. The limit controls after how much
+ *   commands the processing will be stopped. All commands which got processed
+ *   will get removed from the list.
+ *
+ * @param dest Destination to write to, see GPUCommand::write_to
+ * @param limit Maximum amount of commands to process
+ *
+ * @return Amount of commands processed, between 0 and #limit.
+ */
+size_t GPUCommandList::write_commands_to(const PTA_uchar &dest, size_t limit) {
+  size_t num_commands_written = 0;
+
+  while (num_commands_written < limit && !_commands.empty()) {
+    // Write the first command to the stream, and delete it afterwards
+    _commands.front().write_to(dest, num_commands_written);
+    _commands.pop();
+    num_commands_written ++;
+  }
+
+  return num_commands_written;
+}

+ 54 - 0
contrib/src/rplight/gpuCommandList.h

@@ -0,0 +1,54 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef GPUCOMMANDLIST_H
+#define GPUCOMMANDLIST_H
+
+#include "pandabase.h"
+#include "gpuCommand.h"
+
+#include <queue>
+
+/**
+ * @brief Class to store a list of commands.
+ * @details This is a class to store a list of GPUCommands. It provides
+ *   functionality to only provide the a given amount of commands at one time.
+ */
+class GPUCommandList {
+PUBLISHED:
+  GPUCommandList();
+
+  void add_command(const GPUCommand& cmd);
+  size_t get_num_commands();
+  size_t write_commands_to(const PTA_uchar &dest, size_t limit = 32);
+
+  MAKE_PROPERTY(num_commands, get_num_commands);
+
+protected:
+  queue<GPUCommand> _commands;
+};
+
+#endif // GPUCOMMANDLIST_H

+ 233 - 0
contrib/src/rplight/iesDataset.cxx

@@ -0,0 +1,233 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#include "iesDataset.h"
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+
+NotifyCategoryDef(iesdataset, "")
+
+/**
+ * @brief Constructs a new empty dataset.
+ * @details This constructs a new IESDataset with no data set.
+ */
+IESDataset::IESDataset() {
+}
+
+/**
+ * @brief Sets the vertical angles of the dataset.
+ * @details This sets the list of vertical angles of the dataset.
+ *
+ * @param vertical_angles Vector of all vertical angles.
+ */
+void IESDataset::set_vertical_angles(const PTA_float &vertical_angles) {
+  nassertv(vertical_angles.size() > 0);
+	_vertical_angles = vertical_angles;
+}
+
+/**
+ * @brief Sets the horizontal angles of the dataset.
+ * @details This sets the list of horizontal angles of the dataset.
+ *
+ * @param horizontal_angles Vector of all horizontal angles.
+ */
+void IESDataset::set_horizontal_angles(const PTA_float &horizontal_angles) {
+  nassertv(horizontal_angles.size() > 0);
+	_horizontal_angles = horizontal_angles;
+}
+
+/**
+ * @brief Sets the candela values.
+ * @details This sets the candela values of the dataset. They should be an
+ *   interleaved 2D array with the dimensions vertical_angles x horizontal_angles.
+ *   They also should be normalized by dividing by the maximum entry.
+ * @param candela_values Interleaved 2D-vector of candela values.
+ */
+void IESDataset::set_candela_values(const PTA_float &candela_values) {
+	nassertv(candela_values.size() == _horizontal_angles.size() * _vertical_angles.size());
+	_candela_values = candela_values;
+}
+
+/**
+ * @brief Internal method to access the candela data.
+ * @details This lookups a candela value in the candela values. It converts a
+ *   two dimensional index to a onedimensional index and then returns the candela
+ *   value at that position.
+ *
+ * @param vertical_angle_idx Index of the vertical angle
+ * @param horizontal_angle_idx Index of the horizontal angle
+ *
+ * @return Candela value between 0 .. 1
+ */
+float IESDataset::get_candela_value_from_index(size_t vertical_angle_idx, size_t horizontal_angle_idx) const {
+	size_t index = vertical_angle_idx + horizontal_angle_idx * _vertical_angles.size();
+	nassertr(index >= 0 && index < _candela_values.size(), 0.0);
+	return _candela_values[index];
+}
+
+/**
+ * @brief Samples the dataset at the given position
+ * @details This looks up a value in the dataset, by specifying a horizontal and
+ *   vertical angle. This is used for generating the LUT. The vertical and horizontal
+ *   angle should be inside of the bounds of the vertical and horizontal angle arrays.
+ *
+ * @param vertical_angle Vertical angle, from 0 .. 90 or 0 .. 180 depending on the dataset
+ * @param horizontal_angle Horizontal angle, from 0 .. 180 or 0 .. 360 depending on the dataset.
+ *
+ * @return Candela value between 0 .. 1
+ */
+float IESDataset::get_candela_value(float vertical_angle, float horizontal_angle) const {
+
+  // Special case for datasets without horizontal angles
+  if (_horizontal_angles.size() == 1) {
+    return get_vertical_candela_value(0, vertical_angle);
+  }
+
+  float max_angle = _horizontal_angles[_horizontal_angles.size() - 1];
+
+  // Wrap angle to fit from 0 .. 360 degree. Most profiles only distribute
+  // candela values from 0 .. 180 or even 0 .. 90. We have to mirror the
+  // values at those borders (so 2 times for 180 degree and 4 times for 90 degree)
+  horizontal_angle = fmod(horizontal_angle, 2.0f * max_angle);
+  if (horizontal_angle > max_angle) {
+    horizontal_angle = 2.0 * max_angle - horizontal_angle;
+  }
+
+  // Simlar to the vertical step, we now try interpolating a horizontal angle,
+  // but we need to evaluate the vertical value for each row instead of fetching
+  // the value directly
+  for (size_t horizontal_index = 1; horizontal_index < _horizontal_angles.size(); ++horizontal_index) {
+    float curr_angle = _horizontal_angles[horizontal_index];
+
+    if (curr_angle >= horizontal_angle) {
+
+      // Get previous angle data
+      float prev_angle = _horizontal_angles[horizontal_index - 1];
+      float prev_value = get_vertical_candela_value(horizontal_index - 1, vertical_angle);
+      float curr_value = get_vertical_candela_value(horizontal_index, vertical_angle);
+
+      // Interpolate lineary
+      float lerp = (horizontal_angle - prev_angle) / (curr_angle - prev_angle);
+
+      // Should never occur, but to be safe:
+      if (lerp < 0.0 || lerp > 1.0) {
+        iesdataset_cat.error() << "Invalid horizontal lerp: " << lerp
+                     << ", requested angle was " << horizontal_angle
+                     << ", prev = " << prev_angle << ", cur = " << curr_angle
+                     << endl;
+      }
+
+      return curr_value * lerp + prev_value * (1-lerp);
+    }
+  }
+
+  return 0.0;
+}
+
+/**
+ * @brief Fetches a vertical candela value
+ * @details Fetches a vertical candela value, using a given horizontal position.
+ *   This does an 1D interpolation in the candela values array.
+ *
+ * @param horizontal_angle_idx The index of the horizontal angle in the horizontal
+ *   angle array.
+ * @param vertical_angle The vertical angle. Interpolation will be done if the
+ *   vertical angle is not in the vertical angles array.
+ *
+ * @return Candela value between 0 .. 1
+ */
+float IESDataset::get_vertical_candela_value(size_t horizontal_angle_idx, float vertical_angle) const {
+  nassertr(horizontal_angle_idx >= 0 && horizontal_angle_idx < _horizontal_angles.size(), 0.0);
+
+  // Lower bound
+  if (vertical_angle < 0.0) return 0.0;
+
+  // Upper bound
+  if (vertical_angle > _vertical_angles[_vertical_angles.size() - 1] ) return 0.0;
+
+  // Find lowest enclosing angle
+  for (size_t vertical_index = 1; vertical_index < _vertical_angles.size(); ++vertical_index) {
+    float curr_angle = _vertical_angles[vertical_index];
+
+    // Found value
+    if (curr_angle > vertical_angle) {
+
+      // Get previous angle data
+      float prev_angle = _vertical_angles[vertical_index - 1];
+      float prev_value = get_candela_value_from_index(vertical_index - 1, horizontal_angle_idx);
+      float curr_value = get_candela_value_from_index(vertical_index, horizontal_angle_idx);
+
+      // Interpolate lineary
+      float lerp = (vertical_angle - prev_angle) / (curr_angle - prev_angle);
+
+      // Should never occur, but to be safe:
+      if (lerp < 0.0 || lerp > 1.0) {
+        iesdataset_cat.error() << "ERROR: Invalid vertical lerp: " << lerp
+                     << ", requested angle was " << vertical_angle
+                     << ", prev = " << prev_angle << ", cur = " << curr_angle
+                     << endl;
+      }
+
+      return curr_value * lerp + prev_value * (1-lerp);
+    }
+  }
+  return 0.0;
+}
+
+/**
+ * @brief Generates the IES LUT
+ * @details This generates the LUT into a given dataset texture. The x-axis
+ *   referes to the vertical_angle, whereas the y-axis refers to the
+ *   horizontal angle.
+ *
+ * @param dest_tex Texture to write the LUT into
+ * @param z Layer to write the LUT into, in case the texture is a 3D Texture or
+ *   2D Texture Array.
+ */
+void IESDataset::generate_dataset_texture_into(Texture* dest_tex, size_t z) const {
+
+  size_t resolution_vertical = dest_tex->get_y_size();
+  size_t resolution_horizontal = dest_tex->get_x_size();
+
+  // Candla values are stored flippped - vertical angles in the x - Axis
+  // and horizontal angles in the y - Axis
+  PNMImage dest = PNMImage(resolution_vertical, resolution_horizontal, 1, 65535);
+
+  for (size_t vert = 0; vert < resolution_vertical; ++vert) {
+    for (size_t horiz = 0; horiz < resolution_horizontal; ++horiz) {
+      float vert_angle = (float)vert / (float)(resolution_vertical-1);
+      vert_angle = cos(vert_angle * M_PI) * 90.0 + 90.0;
+      float horiz_angle = (float)horiz / (float)(resolution_horizontal-1) * 360.0;
+      float candela = get_candela_value(vert_angle, horiz_angle);
+      dest.set_xel(vert, horiz, candela);
+    }
+  }
+
+
+  dest_tex->load(dest, z, 0);
+}

+ 68 - 0
contrib/src/rplight/iesDataset.h

@@ -0,0 +1,68 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef IESDATASET_H
+#define IESDATASET_H
+
+#include "pandabase.h"
+#include "pta_float.h"
+#include "pointerToArray.h"
+#include "texture.h"
+#include "pnmImage.h"
+
+NotifyCategoryDecl(iesdataset, EXPORT_CLASS, EXPORT_TEMPL);
+
+
+/**
+ * @brief This class generates a LUT from IES data.
+ * @details This class is used by the IESLoader to generate a LUT texture which
+ *   is used in the shaders to perform IES lighting. It takes a set of vertical
+ *   and horizontal angles, as well as a set of candela values, which then are
+ *   lineary interpolated onto a 2D LUT Texture.
+ */
+class IESDataset {
+PUBLISHED:
+  IESDataset();
+
+  void set_vertical_angles(const PTA_float &vertical_angles);
+  void set_horizontal_angles(const PTA_float &horizontal_angles);
+  void set_candela_values(const PTA_float &candela_values);
+
+  void generate_dataset_texture_into(Texture* dest_tex, size_t z) const;
+
+public:
+
+  float get_candela_value(float vertical_angle, float horizontal_angle) const;
+  float get_candela_value_from_index(size_t vertical_angle_idx, size_t horizontal_angle_idx) const;
+  float get_vertical_candela_value(size_t horizontal_angle_idx, float vertical_angle) const;
+
+private:
+  PTA_float _vertical_angles;
+  PTA_float _horizontal_angles;
+  PTA_float _candela_values;
+};
+
+#endif // IESDATASET_H

+ 140 - 0
contrib/src/rplight/internalLightManager.I

@@ -0,0 +1,140 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+/**
+ * @brief Returns the maximum light index
+ * @details This returns the maximum light index (also called slot). Any lights
+ *   after that slot are guaranteed to be zero-lights. This is useful when
+ *   iterating over the list of lights, because iteration can be stopped when
+ *   the maximum light index is reached.
+ *
+ *   The maximum light index points to the last slot which is used. If no lights
+ *   are attached, -1 is returned. If one light is attached at slot 0, the index
+ *   is 0, if two are attached at the slots 0 and 1, the index is 1, and so on.
+ *
+ *   If, for example, two lights are attached at the slots 2 and 5, then the
+ *   index will be 5. Keep in mind that the max-index is not an indicator for
+ *   how many lights are attached. Also, zero lights still may occur when iterating
+ *   over the light lists
+ *
+ * @return Maximum light index
+ */
+inline int InternalLightManager::get_max_light_index() const {
+  return _lights.get_max_index();
+}
+
+/**
+ * @brief Returns the amount of stored lights.
+ * @details This returns the amount of stored lights. This behaves unlike
+ *   InternalLightManager::get_max_light_index, and instead returns the true
+ *   amount of lights, which is completely unrelated to the amount of used slots.
+ *
+ * @return Amount of stored lights
+ */
+inline size_t InternalLightManager::get_num_lights() const {
+  return _lights.get_num_entries();
+}
+
+/**
+ * @brief Returns the amount of shadow sources.
+ * @details This returns the total amount of stored shadow sources. This does
+ *   not denote the amount of updated sources, but instead takes into account
+ *   all sources, even those out of frustum.
+ * @return Amount of shadow sources.
+ */
+inline size_t InternalLightManager::get_num_shadow_sources() const {
+  return _shadow_sources.get_num_entries();
+}
+
+/**
+ * @brief Sets the handle to the shadow manager
+ * @details This sets the handle to the global shadow manager. It is usually
+ *   constructed on the python side, so we need to get a handle to it.
+ *
+ *   The manager should be a handle to a ShadowManager instance, and will be
+ *   stored somewhere on the python side most likely. The light manager does not
+ *   keep a reference to it, so the python side should make sure to keep one.
+ *
+ *   Be sure to call this before the InternalLightManager::update() method is
+ *   called, otherwise an assertion will get triggered.
+ *
+ * @param mgr The ShadowManager instance
+ */
+inline void InternalLightManager::set_shadow_manager(ShadowManager* mgr) {
+  _shadow_manager = mgr;
+}
+
+/**
+ * @brief Sets a handle to the command list
+ * @details This sets a handle to the global GPUCommandList. This is required to
+ *   emit GPUCommands, which are used for attaching and detaching lights, as well
+ *   as shadow source updates.
+ *
+ *   The cmd_list should be a handle to a GPUCommandList handle, and will be
+ *   stored somewhere on the python side most likely. The light manager does not
+ *   keep a reference to it, so the python side should make sure to keep one.
+ *
+ *   Be sure to call this before the InternalLightManager::update() method is
+ *   called, otherwise an assertion will get triggered.
+ *
+ * @param cmd_list The GPUCommandList instance
+ */
+inline void InternalLightManager::set_command_list(GPUCommandList *cmd_list) {
+  _cmd_list = cmd_list;
+}
+
+/**
+ * @brief Sets the camera position
+ * @details This sets the camera position, which will be used to determine which
+ *   shadow sources have to get updated
+ *
+ * @param mat View projection mat
+ */
+inline void InternalLightManager::set_camera_pos(const LPoint3 &pos) {
+  _camera_pos = pos;
+}
+
+/**
+ * @brief Sets the maximum shadow update distance
+ * @details This controls the maximum distance until which shadows are updated.
+ *   If a shadow source is past that distance, it is ignored and no longer recieves
+ *   updates until it is in range again
+ *
+ * @param dist Distance in world space units
+ */
+inline void InternalLightManager::set_shadow_update_distance(PN_stdfloat dist) {
+  _shadow_update_distance = dist;
+}
+
+/**
+ * @brief Returns the internal used ShadowManager
+ * @details This returns a handle to the internally used shadow manager
+ * @return Shadow manager
+ */
+inline ShadowManager* InternalLightManager::get_shadow_manager() const {
+  return _shadow_manager;
+}

+ 441 - 0
contrib/src/rplight/internalLightManager.cxx

@@ -0,0 +1,441 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#include "internalLightManager.h"
+
+#include <algorithm>
+
+NotifyCategoryDef(lightmgr, "");
+
+
+/**
+ * @brief Constructs the light manager
+ * @details This constructs the light manager, initializing the light and shadow
+ *   storage. You should set a command list and shadow manager before calling
+ *   InternalLightManager::update. s
+ */
+InternalLightManager::InternalLightManager() {
+  _shadow_update_distance = 100.0;
+  _cmd_list = nullptr;
+  _shadow_manager = nullptr;
+}
+
+/**
+ * @brief Adds a new light.
+ * @details This adds a new light to the list of lights. This will throw an
+ *   error and return if the light is already attached. You may only call
+ *   this after the ShadowManager was already set.
+ *
+ *   While the light is attached, the light manager keeps a reference to it, so
+ *   the light does not get destructed.
+ *
+ *   This also setups the shadows on the light, in case shadows are enabled.
+ *   While a light is attached, you can not change whether it casts shadows or not.
+ *   To do so, detach the light, change the setting, and re-add the light.
+ *
+ *   In case no free light slot is available, an error will be printed and no
+ *   action will be performed.
+ *
+ *   If no shadow manager was set, an assertion will be triggered.
+ *
+ * @param light The light to add.
+ */
+void InternalLightManager::add_light(PT(RPLight) light) {
+  nassertv(_shadow_manager != nullptr); // Shadow manager not set yet!
+
+  // Don't attach the light in case its already attached
+  if (light->has_slot()) {
+    lightmgr_cat.error() << "could not add light because it already is attached! "
+               << "Detach the light first, then try it again." << endl;
+    return;
+  }
+
+  // Find a free slot
+  size_t slot;
+  if (!_lights.find_slot(slot)) {
+    lightmgr_cat.error() << "Light limit of " << MAX_LIGHT_COUNT << " reached, "
+               << "all light slots used!" << endl;
+    return;
+  }
+
+  // Reference the light because we store it, to avoid it getting destructed
+  // on the python side while we still work with it. The reference will be
+  // removed when the light gets detached.
+  light->ref();
+
+  // Reserve the slot
+  light->assign_slot(slot);
+  _lights.reserve_slot(slot, light);
+
+  // Setup the shadows in case the light uses them
+  if (light->get_casts_shadows()) {
+    setup_shadows(light);
+  }
+
+  // Store the light on the gpu, to make sure the GPU directly knows about it.
+  // We could wait until the next update cycle, but then we might be one frame
+  // too late already.
+  gpu_update_light(light);
+}
+
+/**
+ * @brief Internal method to setup shadows for a light
+ * @details This method gets called by the InternalLightManager::add_light method
+ *   to setup a lights shadow sources, in case shadows are enabled on that light.
+ *
+ *   It finds a slot for all shadow sources of the ilhgt, and inits the shadow
+ *   sources as well. If no slot could be found, an error is printed an nothing
+ *   happens.
+ *
+ * @param light The light to init the shadow sources for
+ */
+void InternalLightManager::setup_shadows(RPLight* light) {
+
+  // Init the lights shadow sources, and also call update once to make sure
+  // the sources are properly initialized
+  light->init_shadow_sources();
+  light->update_shadow_sources();
+
+  // Find consecutive slots, this is important for PointLights so we can just
+  // store the first index of the source, and get the other slots by doing
+  // first_index + 1, +2 and so on.
+  size_t base_slot;
+  size_t num_sources = light->get_num_shadow_sources();
+  if (!_shadow_sources.find_consecutive_slots(base_slot, num_sources)) {
+    lightmgr_cat.error() << "Failed to find slot for shadow sources! "
+               << "Shadow-Source limit of " << MAX_SHADOW_SOURCES
+               << " reached!" << endl;
+    return;
+  }
+
+  // Init all sources
+  for (int i = 0; i < num_sources; ++i) {
+    ShadowSource* source = light->get_shadow_source(i);
+
+    // Set the source as dirty, so it gets updated in the beginning
+    source->set_needs_update(true);
+
+    // Assign the slot to the source. Since we got consecutive slots, we can
+    // just do base_slot + N.
+    size_t slot = base_slot + i;
+    _shadow_sources.reserve_slot(slot, source);
+    source->set_slot(slot);
+  }
+}
+
+/**
+ * @brief Removes a light
+ * @details This detaches a light. This prevents it from being rendered, and also
+ *   cleans up all resources used by that light. If no reference is kept on the
+ *   python side, the light will also get destructed.
+ *
+ *   If the light was not previously attached with InternalLightManager::add_light,
+ *   an error will be triggered and nothing happens.
+ *
+ *   In case the light was set to cast shadows, all shadow sources are cleaned
+ *   up, and their regions in the shadow atlas are freed.
+ *
+ *   All resources used by the light in the light and shadow storage are also
+ *   cleaned up, by emitting cleanup GPUCommands.
+ *
+ *   If no shadow manager was set, an assertion will be triggered.
+ *
+ * @param light [description]
+ */
+void InternalLightManager::remove_light(PT(RPLight) light) {
+  nassertv(_shadow_manager != nullptr);
+
+  if (!light->has_slot()) {
+    lightmgr_cat.error() << "Could not detach light, light was not attached!" << endl;
+    return;
+  }
+
+  // Free the lights slot in the light storage
+  _lights.free_slot(light->get_slot());
+
+  // Tell the GPU we no longer need the lights data
+  gpu_remove_light(light);
+
+  // Mark the light as detached. After this call, we can not call get_slot
+  // anymore, so its important we do this after we unregistered the light
+  // from everywhere.
+  light->remove_slot();
+
+  // Clear shadow related stuff, in case the light casts shadows
+  if (light->get_casts_shadows()) {
+
+    // Free the slots of all sources, and also unregister their regions from
+    // the shadow atlas.
+    for (size_t i = 0; i < light->get_num_shadow_sources(); ++i) {
+      ShadowSource* source = light->get_shadow_source(i);
+      if (source->has_slot()) {
+        _shadow_sources.free_slot(source->get_slot());
+      }
+      if (source->has_region()) {
+        _shadow_manager->get_atlas()->free_region(source->get_region());
+        source->clear_region();
+      }
+    }
+
+    // Remove all sources of the light by emitting a consecutive remove command
+    gpu_remove_consecutive_sources(light->get_shadow_source(0),
+                     light->get_num_shadow_sources());
+
+    // Finally remove all shadow sources. This is important in case the light
+    // will be re-attached. Otherwise an assertion will get triggered.
+    light->clear_shadow_sources();
+  }
+
+  // Since we referenced the light when we stored it, we have to decrease
+  // the reference now. In case no reference was kept on the python side,
+  // the light will get destructed soon.
+  light->unref();
+}
+
+/**
+ * @brief Internal method to remove consecutive sources from the GPU.
+ * @details This emits a GPUCommand to consecutively remove shadow sources from
+ *   the GPU. This is called when a light gets removed, to free the space its
+ *   shadow sources took. Its not really required, because as long as the light
+ *   is not used, there is no reference to the sources. However, it can't hurt to
+ *   cleanup the memory.
+ *
+ *   All sources starting at first_source->get_slot() until
+ *   first_source->get_slot() + num_sources will get cleaned up.
+ *
+ * @param first_source First source of the light
+ * @param num_sources Amount of consecutive sources to clear
+ */
+void InternalLightManager::gpu_remove_consecutive_sources(ShadowSource *first_source,
+                              size_t num_sources) {
+  nassertv(_cmd_list != nullptr);    // No command list set yet
+  nassertv(first_source->has_slot()); // Source has no slot!
+  GPUCommand cmd_remove(GPUCommand::CMD_remove_sources);
+  cmd_remove.push_int(first_source->get_slot());
+  cmd_remove.push_int(num_sources);
+  _cmd_list->add_command(cmd_remove);
+}
+
+/**
+ * @brief Internal method to remove a light from the GPU.
+ * @details This emits a GPUCommand to clear a lights data. This sets the data
+ *   to all zeros, marking that no light is stored anymore.
+ *
+ *   This throws an assertion in case the light is not currently attached. Be
+ *   sure to call this before detaching the light.
+ *
+ * @param light The light to remove, must be attached.
+ */
+void InternalLightManager::gpu_remove_light(RPLight* light) {
+  nassertv(_cmd_list != nullptr);  // No command list set yet
+  nassertv(light->has_slot());  // Light has no slot!
+  GPUCommand cmd_remove(GPUCommand::CMD_remove_light);
+  cmd_remove.push_int(light->get_slot());
+  _cmd_list->add_command(cmd_remove);
+}
+
+/**
+ * @brief Updates a lights data on the GPU
+ * @details This method emits a GPUCommand to update a lights data. This can
+ *   be used to initially store the lights data, or to update the data whenever
+ *   the light changed.
+ *
+ *   This throws an assertion in case the light is not currently attached. Be
+ *   sure to call this after attaching the light.
+ *
+ * @param light The light to update
+ */
+void InternalLightManager::gpu_update_light(RPLight* light) {
+  nassertv(_cmd_list != nullptr);  // No command list set yet
+  nassertv(light->has_slot());  // Light has no slot!
+  GPUCommand cmd_update(GPUCommand::CMD_store_light);
+  cmd_update.push_int(light->get_slot());
+  light->write_to_command(cmd_update);
+  light->set_needs_update(false);
+  _cmd_list->add_command(cmd_update);
+}
+
+/**
+ * @brief Updates a shadow source data on the GPU
+ * @details This emits a GPUCommand to update a given shadow source, storing all
+ *   data of the source on the GPU. This can also be used to initially store a
+ *   ShadowSource, since all data will be overridden.
+ *
+ *   This throws an assertion if the source has no slot yet.
+ *
+ * @param source The source to update
+ */
+void InternalLightManager::gpu_update_source(ShadowSource* source) {
+  nassertv(_cmd_list != nullptr);  // No command list set yet
+  nassertv(source->has_slot()); // Source has no slot!
+  GPUCommand cmd_update(GPUCommand::CMD_store_source);
+  cmd_update.push_int(source->get_slot());
+  source->write_to_command(cmd_update);
+  _cmd_list->add_command(cmd_update);
+}
+
+/**
+ * @brief Internal method to update all lights
+ * @details This is called by the main update method, and iterates over the list
+ *   of lights. If a light is marked as dirty, it will recieve an update of its
+ *   data and its shadow sources.
+ */
+void InternalLightManager::update_lights() {
+  for (auto iter = _lights.begin(); iter != _lights.end(); ++iter) {
+    RPLight* light = *iter;
+    if (light && light->get_needs_update()) {
+      if (light->get_casts_shadows()) {
+        light->update_shadow_sources();
+      }
+      gpu_update_light(light);
+    }
+  }
+}
+
+/**
+ * @brief Compares shadow sources by their priority
+ * @details Returns if a has a greater priority than b. This depends on the
+ *   resolution of the source, and also if the source has a region or not.
+ *   This method can be passed to std::sort.
+ *
+ * @param a First source
+ * @param b Second source
+ *
+ * @return true if a is more important than b, else false
+ */
+bool InternalLightManager::compare_shadow_sources(const ShadowSource* a, const ShadowSource* b) const {
+
+  // Make sure that sources which already have a region (but maybe outdated)
+  // come after sources which have no region at all.
+  if (a->has_region() != b->has_region()) {
+    return b->has_region();
+  }
+
+  // Compare sources based on their distance to the camera
+  PN_stdfloat dist_a = (_camera_pos - a->get_bounds().get_center()).length_squared();
+  PN_stdfloat dist_b = (_camera_pos - a->get_bounds().get_center()).length_squared();
+
+  // XXX: Should also compare based on source size, so that huge sources recieve
+  // more updates
+
+  return dist_b > dist_a;
+}
+
+/**
+ * @brief Internal method to update all shadow sources
+ * @details This updates all shadow sources which are marked dirty. It will sort
+ *   the list of all dirty shadow sources by their resolution, take the first
+ *   n entries, and update them. The amount of sources processed depends on the
+ *   max_updates of the ShadowManager.
+ */
+void InternalLightManager::update_shadow_sources() {
+
+  // Find all dirty shadow sources and make a list of them
+  vector<ShadowSource*> sources_to_update;
+   for (auto iter = _shadow_sources.begin(); iter != _shadow_sources.end(); ++iter) {
+    ShadowSource* source = *iter;
+    if (source) {
+      const BoundingSphere& bounds = source->get_bounds();
+
+      // Check if source is in range
+      PN_stdfloat distance_to_camera = (_camera_pos - bounds.get_center()).length() - bounds.get_radius();
+      if (distance_to_camera < _shadow_update_distance) {
+        if (source->get_needs_update()) {
+          sources_to_update.push_back(source);
+        }
+      } else {
+
+        // Free regions of sources which are out of the update radius,
+        // to make space for other regions
+        if (source->has_region()) {
+          _shadow_manager->get_atlas()->free_region(source->get_region());
+          source->clear_region();
+        }
+      }
+    }
+
+  }
+
+  // Sort the sources based on their importance, so that sources with a bigger
+  // priority come first. This helps to get a better packing on the shadow atlas.
+  // However, we also need to prioritize sources which have no current region,
+  // because no shadows are worse than outdated-shadows.
+  std::sort(sources_to_update.begin(), sources_to_update.end(), [this](const ShadowSource* a, const ShadowSource* b) {
+    return this->compare_shadow_sources(a, b);
+  });
+
+  // Get a handle to the atlas, will be frequently used
+  ShadowAtlas *atlas = _shadow_manager->get_atlas();
+
+  // Free the regions of all sources which will get updated. We have to take into
+  // account that only a limited amount of sources can get updated per frame.
+  size_t update_slots = min(sources_to_update.size(),
+                _shadow_manager->get_num_update_slots_left());
+  for(size_t i = 0; i < update_slots; ++i) {
+    if (sources_to_update[i]->has_region()) {
+       atlas->free_region(sources_to_update[i]->get_region());
+    }
+  }
+
+  // Find an atlas spot for all regions which are supposed to get an update
+  for (size_t i = 0; i < update_slots; ++i) {
+    ShadowSource *source = sources_to_update[i];
+
+    if(!_shadow_manager->add_update(source)) {
+      // In case the ShadowManager lied about the number of updates left
+      lightmgr_cat.error() << "ShadowManager ensured update slot, but slot is taken!" << endl;
+      break;
+    }
+
+    // We have an update slot, and are guaranteed to get updated as soon
+    // as possible, so we can start getting a new atlas position.
+    size_t region_size = atlas->get_required_tiles(source->get_resolution());
+    LVecBase4i new_region = atlas->find_and_reserve_region(region_size, region_size);
+    LVecBase4 new_uv_region = atlas->region_to_uv(new_region);
+    source->set_region(new_region, new_uv_region);
+
+    // Mark the source as updated
+    source->set_needs_update(false);
+    gpu_update_source(source);
+  }
+}
+
+/**
+ * @brief Main update method
+ * @details This is the main update method of the InternalLightManager. It
+ *   processes all lights and shadow sources, updates them, and notifies the
+ *   GPU about it. This should be called on a per-frame basis.
+ *
+ *   If the InternalLightManager was not initialized yet, an assertion is thrown.
+ */
+void InternalLightManager::update() {
+  nassertv(_shadow_manager != nullptr); // Not initialized yet!
+  nassertv(_cmd_list != nullptr);     // Not initialized yet!
+
+  update_lights();
+  update_shadow_sources();
+}

+ 102 - 0
contrib/src/rplight/internalLightManager.h

@@ -0,0 +1,102 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef INTERNALLIGHTMANAGER_H
+#define INTERNALLIGHTMANAGER_H
+
+#include "referenceCount.h"
+#include "rpLight.h"
+#include "shadowSource.h"
+#include "shadowAtlas.h"
+#include "shadowManager.h"
+#include "pointerSlotStorage.h"
+#include "gpuCommandList.h"
+
+#define MAX_LIGHT_COUNT 65535
+#define MAX_SHADOW_SOURCES 2048
+
+NotifyCategoryDecl(lightmgr, EXPORT_CLASS, EXPORT_TEMPL);
+
+/**
+ * @brief Internal class used for handling lights and shadows.
+ * @details This is the internal class used by the pipeline to handle all
+ *   lights and shadows. It stores references to the lights, manages handling
+ *   the light and shadow slots, and also communicates with the GPU with the
+ *   GPUCommandQueue to store light and shadow source data.
+ */
+class InternalLightManager {
+PUBLISHED:
+  InternalLightManager();
+
+  void add_light(PT(RPLight) light);
+  void remove_light(PT(RPLight) light);
+
+  void update();
+  inline void set_camera_pos(const LPoint3 &pos);
+  inline void set_shadow_update_distance(PN_stdfloat dist);
+
+  inline int get_max_light_index() const;
+  MAKE_PROPERTY(max_light_index, get_max_light_index);
+
+  inline size_t get_num_lights() const;
+  MAKE_PROPERTY(num_lights, get_num_lights);
+
+  inline size_t get_num_shadow_sources() const;
+  MAKE_PROPERTY(num_shadow_sources, get_num_shadow_sources);
+
+  inline void set_shadow_manager(ShadowManager* mgr);
+  inline ShadowManager* get_shadow_manager() const;
+  MAKE_PROPERTY(shadow_manager, get_shadow_manager, set_shadow_manager);
+
+  inline void set_command_list(GPUCommandList *cmd_list);
+
+protected:
+
+  void gpu_update_light(RPLight* light);
+  void gpu_update_source(ShadowSource* source);
+  void gpu_remove_light(RPLight* light);
+  void gpu_remove_consecutive_sources(ShadowSource *first_source, size_t num_sources);
+
+  void setup_shadows(RPLight* light);
+  bool compare_shadow_sources(const ShadowSource* a, const ShadowSource* b) const;
+
+  void update_lights();
+  void update_shadow_sources();
+
+  GPUCommandList* _cmd_list;
+  ShadowManager* _shadow_manager;
+
+  PointerSlotStorage<RPLight*, MAX_LIGHT_COUNT> _lights;
+  PointerSlotStorage<ShadowSource*, MAX_SHADOW_SOURCES> _shadow_sources;
+
+  LPoint3 _camera_pos;
+  float _shadow_update_distance;
+
+};
+
+#include "internalLightManager.I"
+
+#endif // INTERNALLIGHTMANAGER_H

+ 13 - 0
contrib/src/rplight/p3rplight_composite1.cxx

@@ -0,0 +1,13 @@
+#include "config_rplight.cxx"
+#include "gpuCommand.cxx"
+#include "gpuCommandList.cxx"
+#include "iesDataset.cxx"
+#include "internalLightManager.cxx"
+#include "pssmCameraRig.cxx"
+#include "rpLight.cxx"
+#include "rpPointLight.cxx"
+#include "rpSpotLight.cxx"
+#include "shadowAtlas.cxx"
+#include "shadowManager.cxx"
+#include "shadowSource.cxx"
+#include "tagStateManager.cxx"

+ 239 - 0
contrib/src/rplight/pointerSlotStorage.h

@@ -0,0 +1,239 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef POINTERSLOTSTORAGE_H
+#define POINTERSLOTSTORAGE_H
+
+
+#ifdef CPPPARSER
+
+// Dummy implementation for interrogate
+template <typename T, int SIZE>
+class PointerSlotStorage {};
+
+#else // CPPPARSER
+
+
+#include "pandabase.h"
+
+// Apple has an outdated libstdc++, so pull the class from TR1.
+#if defined(__GLIBCXX__) && __GLIBCXX__ <= 20070719
+#include <tr1/array>
+using std::tr1::array;
+#else
+#include <array>
+#endif
+
+/**
+ * @brief Class to keep a list of pointers and nullpointers.
+ * @details This class stores a fixed size list of pointers, whereas pointers
+ *   may be a nullptr as well. It provides functionality to find free slots,
+ *   and also to find free consecutive slots, as well as taking care of reserving slots.
+ *
+ * @tparam T* Pointer-Type
+ * @tparam SIZE Size of the storage
+ */
+template <typename T, int SIZE>
+class PointerSlotStorage {
+public:
+  /**
+   * @brief Constructs a new PointerSlotStorage
+   * @details This constructs a new PointerSlotStorage, with all slots
+   *   initialized to a nullptr.
+   */
+  PointerSlotStorage() {
+#if defined(__GLIBCXX__) && __GLIBCXX__ <= 20070719
+    _data.assign(nullptr);
+#else
+    _data.fill(nullptr);
+#endif
+    _max_index = 0;
+    _num_entries = 0;
+  }
+
+  /**
+   * @brief Returns the maximum index of the container
+   * @details This returns the greatest index of any element which is not zero.
+   *   This can be useful for iterating the container, since all elements
+   *   coming after the returned index are guaranteed to be a nullptr.
+   *
+   *   If no elements are in this container, -1 is returned.
+   * @return Maximum index of the container
+   */
+  int get_max_index() const {
+    return _max_index;
+  }
+
+  /**
+   * @brief Returns the amount of elements of the container
+   * @details This returns the amount of elements in the container which are
+   *   no nullptr.
+   * @return Amount of elements
+   */
+  size_t get_num_entries() const {
+    return _num_entries;
+  }
+
+  /**
+   * @brief Finds a free slot
+   * @details This finds the first slot which is a nullptr and returns it.
+   *   This is most likely useful in combination with reserve_slot.
+   *
+   *   When no slot found was found, slot will be undefined, and false will
+   *   be returned.
+   *
+   * @param slot Output-Variable, slot will be stored there
+   * @return true if a slot was found, otherwise false
+   */
+  bool find_slot(size_t &slot) const {
+    for (size_t i = 0; i < SIZE; ++i) {
+      if (_data[i] == nullptr) {
+        slot = i;
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * @brief Finds free consecutive slots
+   * @details This behaves like find_slot, but it tries to find a slot
+   *   after which <num_consecutive-1> free slots follow as well.
+   *
+   *   When no slot found was found, slot will be undefined, and false will
+   *   be returned.
+   *
+   * @param slot Output-Variable, index of the first slot of the consecutive
+   *   slots will be stored there.
+   * @param num_consecutive Amount of consecutive slots to find, including the
+   *   first slot.
+   *
+   * @return true if consecutive slots were found, otherwise false.
+   */
+  bool find_consecutive_slots(size_t &slot, size_t num_consecutive) const {
+    nassertr(num_consecutive > 0, false);
+
+    // Fall back to default search algorithm in case the parameters are equal
+    if (num_consecutive == 1) {
+      return find_slot(slot);
+    }
+
+    // Try to find consecutive slots otherwise
+    for (size_t i = 0; i < SIZE; ++i) {
+      bool any_taken = false;
+      for (size_t k = 0; !any_taken && k < num_consecutive; ++k) {
+        any_taken = _data[i + k] != nullptr;
+      }
+      if (!any_taken) {
+        slot = i;
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * @brief Frees an allocated slot
+   * @details This frees an allocated slot. If the slot was already freed
+   *   before, this method throws an assertion.
+   *
+   * @param slot Slot to free
+   */
+  void free_slot(size_t slot) {
+    nassertv(slot >= 0 && slot < SIZE);
+    nassertv(_data[slot] != nullptr); // Slot was already empty!
+    _data[slot] = nullptr;
+    _num_entries--;
+
+    // Update maximum index
+    if (slot == _max_index) {
+      while (_max_index >= 0 && !_data[_max_index--]);
+    }
+  }
+
+  /**
+   * @brief Frees consecutive allocated slots
+   * @details This behaves like PointerSlotStorage::free_slot, but deletes
+   *   consecutive slots.
+   *
+   * @param slot Start of the consecutive slots to free
+   * @param num_consecutive Number of consecutive slots
+   */
+  void free_consecutive_slots(size_t slot, size_t num_consecutive) {
+    for (size_t i = slot; i < slot + num_consecutive; ++i) {
+      free_slot(i);
+    }
+  }
+
+  /**
+   * @brief Reserves a slot
+   * @details This reserves a slot by storing a pointer in it. If the slot
+   *   was already taken, throws an assertion.
+   *   If the ptr is a nullptr, also throws an assertion.
+   *   If the slot was out of bounds, also throws an assertion.
+   *
+   * @param slot Slot to reserve
+   * @param ptr Pointer to store
+   */
+  void reserve_slot(size_t slot, T ptr) {
+    nassertv(slot >= 0 && slot < SIZE);
+    nassertv(_data[slot] == nullptr); // Slot already taken!
+    nassertv(ptr != nullptr); // nullptr passed as argument!
+    _max_index = max(_max_index, (int)slot);
+    _data[slot] = ptr;
+    _num_entries++;
+  }
+
+  typedef array<T, SIZE> InternalContainer;
+
+  /**
+   * @brief Returns an iterator to the begin of the container
+   * @details This returns an iterator to the beginning of the container
+   * @return Begin-Iterator
+   */
+  typename InternalContainer::iterator begin() {
+    return _data.begin();
+  }
+
+  /**
+   * @brief Returns an iterator to the end of the container
+   * @details This returns an iterator to the end of the iterator. This only
+   *   iterates to PointerSlotStorage::get_max_index()
+   * @return [description]
+   */
+  typename InternalContainer::iterator end() {
+    return _data.begin() + _max_index + 1;
+  }
+
+private:
+  int _max_index;
+  size_t _num_entries;
+  InternalContainer _data;
+};
+
+#endif // CPPPARSER
+
+#endif // POINTERSLOTSTORAGE_H

+ 243 - 0
contrib/src/rplight/pssmCameraRig.I

@@ -0,0 +1,243 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+/**
+ * @brief Sets the maximum pssm distance.
+ * @details This sets the maximum distance in world space until which shadows
+ *   are rendered. After this distance, no shadows will be rendered.
+ *
+ *   If the distance is below zero, an assertion is triggered.
+ *
+ * @param distance Maximum distance in world space
+ */
+inline void PSSMCameraRig::set_pssm_distance(float distance) {
+  nassertv(distance > 0.0 && distance < 100000.0);
+  _pssm_distance = distance;
+}
+
+/**
+ * @brief Sets the suns distance
+ * @details This sets the distance the cameras will have from the cameras frustum.
+ *   This prevents far objects from having no shadows, which can occur when these
+ *   objects are between the cameras frustum and the sun, but not inside of the
+ *   cameras frustum. Setting the sun distance high enough will move the cameras
+ *   away from the camera frustum, being able to cover those distant objects too.
+ *
+ *   If the sun distance is set too high, artifacts will occur due to the reduced
+ *   range of depth. If a value below zero is passed, an assertion will get
+ *   triggered.
+ *
+ * @param distance The sun distance
+ */
+inline void PSSMCameraRig::set_sun_distance(float distance) {
+  nassertv(distance > 0.0 && distance < 100000.0);
+  _sun_distance = distance;
+}
+
+/**
+ * @brief Sets the logarithmic factor
+ * @details This sets the logarithmic factor, which is the core of the algorithm.
+ *   PSSM splits the camera frustum based on a linear and a logarithmic factor.
+ *   While a linear factor provides a good distribution, it often is not applicable
+ *   for wider distances. A logarithmic distribution provides a better distribution
+ *   at distance, but suffers from splitting in the near areas.
+ *
+ *   The logarithmic factor mixes the logarithmic and linear split distribution,
+ *   to get the best of both. A greater factor will make the distribution more
+ *   logarithmic, while a smaller factor will make it more linear.
+ *
+ *   If the factor is below zero, an ssertion is triggered.
+ *
+ * @param factor The logarithmic factor
+ */
+inline void PSSMCameraRig::set_logarithmic_factor(float factor) {
+  nassertv(factor > 0.0);
+  _logarithmic_factor = factor;
+}
+
+/**
+ * @brief Sets whether to use a fixed film size
+ * @details This controls if a fixed film size should be used. This will cause
+ *   the camera rig to cache the current film size, and only change it in case
+ *   it gets too small. This provides less flickering when moving, because the
+ *   film size will stay roughly constant. However, to prevent the cached film
+ *   size getting too big, one should call PSSMCameraRig::reset_film_size
+ *   once in a while, otherwise there might be a lot of wasted space.
+ *
+ * @param flag Whether to use a fixed film size
+ */
+inline void PSSMCameraRig::set_use_fixed_film_size(bool flag) {
+  _use_fixed_film_size = flag;
+}
+
+/**
+ * @brief Sets the resolution of each split
+ * @details This sets the resolution of each split. Currently it is equal for
+ *   each split. This is required when using PSSMCameraRig::set_use_stable_csm,
+ *   to compute how bix a texel is.
+ *
+ *   It has to match the y-resolution of the pssm shadow map. If an invalid
+ *   resolution is triggered, an assertion is thrown.
+ *
+ * @param resolution The resolution of each split.
+ */
+inline void PSSMCameraRig::set_resolution(size_t resolution) {
+  nassertv(resolution >= 0 && resolution < 65535);
+  _resolution = resolution;
+}
+
+/**
+ * @brief Sets whether to use stable CSM snapping.
+ * @details This option controls if stable CSM snapping should be used. When the
+ *   option is enabled, all splits will snap to their texels, so that when moving,
+ *   no flickering will occur. However, this only works when the splits do not
+ *   change their film size, rotation and angle.
+ *
+ * @param flag Whether to use stable CSM snapping
+ */
+inline void PSSMCameraRig::set_use_stable_csm(bool flag) {
+  _use_stable_csm = flag;
+}
+
+/**
+ * @brief Sets the border bias for each split
+ * @details This sets the border bias for every split. This increases each
+ *   splits frustum by multiplying it by (1 + bias), and helps reducing artifacts
+ *   at the borders of the splits. Artifacts can occur when the bias is too low,
+ *   because then the filtering will go over the bounds of the split, producing
+ *   invalid results.
+ *
+ *   If the bias is below zero, an assertion is thrown.
+ *
+ * @param bias Border bias
+ */
+inline void PSSMCameraRig::set_border_bias(float bias) {
+  nassertv(bias >= 0.0);
+  _border_bias = bias;
+}
+
+/**
+ * @brief Resets the film size cache
+ * @details In case PSSMCameraRig::set_use_fixed_film_size is used, this resets
+ *   the film size cache. This might lead to a small "jump" in the shadows,
+ *   because the film size changes, however it leads to a better shadow distribution.
+ *
+ *   This is the case because when using a fixed film size, the cache will get
+ *   bigger and bigger, whenever the camera moves to a grazing angle. However,
+ *   when moving back to a normal angle, the film size cache still stores this
+ *   big angle, and thus the splits will have a much bigger film size than actualy
+ *   required. To prevent this, call this method once in a while, so an optimal
+ *   distribution is ensured.
+ */
+inline void PSSMCameraRig::reset_film_size_cache() {
+  for (size_t i = 0; i < _max_film_sizes.size(); ++i) {
+    _max_film_sizes[i].fill(0);
+  }
+}
+
+/**
+ * @brief Returns the n-th camera
+ * @details This returns the n-th camera of the camera rig, which can be used
+ *   for various stuff like showing its frustum, passing it as a shader input,
+ *   and so on.
+ *
+ *   The first camera is the camera which is the camera of the first split,
+ *   which is the split closest to the camera. All cameras follow in descending
+ *   order until to the last camera, which is the split furthest away from the
+ *   camera.
+ *
+ *   If an invalid index is passed, an assertion is thrown.
+ *
+ * @param index Index of the camera.
+ * @return [description]
+ */
+inline NodePath PSSMCameraRig::get_camera(size_t index) {
+  nassertr(index >= 0 && index < _cam_nodes.size(), NodePath());
+  return _cam_nodes[index];
+}
+
+/**
+ * @brief Internal method to compute the distance of a split
+ * @details This is the internal method to perform the weighting of the
+ *   logarithmic and linear distribution. It computes the distance to the
+ *   camera from which a given split starts, by weighting the logarithmic and
+ *   linear factor.
+ *
+ *   The return value is a value ranging from 0 .. 1. To get the distance in
+ *   world space, the value has to get multiplied with the maximum shadow distance.
+ *
+ * @param split_index The index of the split
+ * @return Distance of the split, ranging from 0 .. 1
+ */
+inline float PSSMCameraRig::get_split_start(size_t split_index) {
+  float x = (float)split_index / (float)_cam_nodes.size();
+  return (exp(_logarithmic_factor*x)-1) / (exp(_logarithmic_factor)-1);
+}
+
+/**
+ * @brief Internal method for interpolating a point along the camera frustum
+ * @details This method takes a given distance in the 0 .. 1 range, whereas
+ *   0 denotes the camera near plane, and 1 denotes the camera far plane,
+ *   and lineary interpolates between them.
+ *
+ * @param origin Edge of the frustum
+ * @param depth Distance in the 0 .. 1 range
+ *
+ * @return interpolated point in world space
+ */
+inline LPoint3 PSSMCameraRig::get_interpolated_point(CoordinateOrigin origin, float depth) {
+  nassertr(depth >= 0.0 && depth <= 1.0, LPoint3());
+  return _curr_near_points[origin] * (1.0 - depth) + _curr_far_points[origin] * depth;
+}
+
+/**
+ * @brief Returns a handle to the MVP array
+ * @details This returns a handle to the array of view-projection matrices
+ *   of the different splits. This can be used for computing shadows. The array
+ *   is a PTALMatrix4 and thus can be directly bound to a shader.
+ *
+ * @return view-projection matrix array
+ */
+inline const PTA_LMatrix4 &PSSMCameraRig::get_mvp_array() {
+  return _camera_mvps;
+}
+
+/**
+ * @brief Returns a handle to the near and far planes array
+ * @details This returns a handle to the near and far plane array. Each split
+ *   has an entry in the array, whereas the x component of the vecto denotes the
+ *   near plane, and the y component denotes the far plane of the split.
+ *
+ *   This is required because the near and far planes of the splits change
+ *   constantly. To access them in a shader, the shader needs access to the
+ *   array.
+ *
+ * @return Array of near and far planes
+ */
+inline const PTA_LVecBase2 &PSSMCameraRig::get_nearfar_array() {
+  return _camera_nearfar;
+}

+ 396 - 0
contrib/src/rplight/pssmCameraRig.cxx

@@ -0,0 +1,396 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#include "pssmCameraRig.h"
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include "orthographicLens.h"
+
+
+PStatCollector PSSMCameraRig::_update_collector("App:Show code:RP_PSSM_update");
+
+/**
+ * @brief Constructs a new PSSM camera rig
+ * @details This constructs a new camera rig, with a given amount of splits.
+ *   The splits can not be changed later on. Splits are also called Cascades.
+ *
+ *   An assertion will be triggered if the splits are below zero.
+ *
+ * @param num_splits Amount of PSSM splits
+ */
+PSSMCameraRig::PSSMCameraRig(size_t num_splits) {
+  nassertv(num_splits > 0);
+  _num_splits = num_splits;
+  _pssm_distance = 100.0;
+  _sun_distance = 500.0;
+  _use_fixed_film_size = false;
+  _use_stable_csm = true;
+  _logarithmic_factor = 1.0;
+  _resolution = 512;
+  _border_bias = 0.1;
+  _camera_mvps = PTA_LMatrix4::empty_array(num_splits);
+  _camera_nearfar = PTA_LVecBase2::empty_array(num_splits);
+  init_cam_nodes();
+}
+
+/**
+ * @brief Destructs the camera rig
+ * @details This destructs the camera rig, cleaning up all used resources.
+ */
+PSSMCameraRig::~PSSMCameraRig() {
+  // TODO: Detach all cameras and call remove_node. Most likely this is not
+  // an issue tho, because the camera rig will never get destructed.
+}
+
+/**
+ * @brief Internal method to init the cameras
+ * @details This method constructs all cameras and their required lens nodes
+ *   for all splits. It also resets the film size array.
+ */
+void PSSMCameraRig::init_cam_nodes() {
+  _cam_nodes.reserve(_num_splits);
+  _max_film_sizes.resize(_num_splits);
+  _cameras.resize(_num_splits);
+  for (size_t i = 0; i < _num_splits; ++i)
+  {
+    // Construct a new lens
+    Lens *lens = new OrthographicLens();
+    lens->set_film_size(1, 1);
+    lens->set_near_far(1, 1000);
+
+    // Construct a new camera
+    _cameras[i] = new Camera("pssm-cam-" + format_string(i), lens);
+    _cam_nodes.push_back(NodePath(_cameras[i]));
+    _max_film_sizes[i].fill(0);
+  }
+}
+
+/**
+ * @brief Reparents the camera rig
+ * @details This reparents all cameras to the given parent. Usually the parent
+ *   will be ShowBase.render. The parent should be the same node where the
+ *   main camera is located in, too.
+ *
+ *   If an empty parrent is passed, an assertion will get triggered.
+ *
+ * @param parent Parent node path
+ */
+void PSSMCameraRig::reparent_to(NodePath parent) {
+  nassertv(!parent.is_empty());
+  for (size_t i = 0; i < _num_splits; ++i) {
+    _cam_nodes[i].reparent_to(parent);
+  }
+  _parent = parent;
+}
+
+/**
+ * @brief Internal method to compute the view-projection matrix of a camera
+ * @details This returns the view-projection matrix of the given split. No bounds
+ *   checking is done. If an invalid index is passed, undefined behaviour occurs.
+ *
+ * @param split_index Index of the split
+ * @return view-projection matrix of the split
+ */
+LMatrix4 PSSMCameraRig::compute_mvp(size_t split_index) {
+  LMatrix4 transform = _parent.get_transform(_cam_nodes[split_index])->get_mat();
+  return transform * _cameras[split_index]->get_lens()->get_projection_mat();
+}
+
+/**
+ * @brief Internal method used for stable CSM
+ * @details This method is used when stable CSM is enabled. It ensures that each
+ *   source only moves in texel-steps, thus preventing flickering. This works by
+ *   projecting the point (0, 0, 0) to NDC space, making sure that it gets projected
+ *   to a texel center, and then projecting that texel back.
+ *
+ *   This only works if the camera does not rotate, change its film size, or change
+ *   its angle.
+ *
+ * @param mat view-projection matrix of the camera
+ * @param resolution resolution of the split
+ *
+ * @return Offset to add to the camera position to achieve stable snapping
+ */
+LVecBase3 PSSMCameraRig::get_snap_offset(const LMatrix4& mat, size_t resolution) {
+  // Transform origin to camera space
+  LPoint4 base_point = mat.get_row(3) * 0.5 + 0.5;
+
+  // Compute the snap offset
+  float texel_size = 1.0 / (float)(resolution);
+  float offset_x = fmod(base_point.get_x(), texel_size);
+  float offset_y = fmod(base_point.get_y(), texel_size);
+
+  // Reproject the offset back, for that we need the inverse MVP
+  LMatrix4 inv_mat(mat);
+  inv_mat.invert_in_place();
+  LVecBase3 new_base_point = inv_mat.xform_point(LVecBase3(
+      (base_point.get_x() - offset_x) * 2.0 - 1.0,
+      (base_point.get_y() - offset_y) * 2.0 - 1.0,
+      base_point.get_z() * 2.0 - 1.0
+    ));
+  return -new_base_point;
+}
+
+/**
+ * @brief Computes the average of a list of points
+ * @details This computes the average over a given set of points in 3D space.
+ *   It returns the average of those points, namely sum_of_points / num_points.
+ *
+ *   It is designed to work with a frustum, which is why it takes two arrays
+ *   with a dimension of 4. Usually the first array are the camera near points,
+ *   and the second array are the camera far points.
+ *
+ * @param starts First array of points
+ * @param ends Second array of points
+ * @return Average of points
+ */
+LPoint3 get_average_of_points(LVecBase3 const (&starts)[4], LVecBase3 const (&ends)[4]) {
+  LPoint3 mid_point(0, 0, 0);
+  for (size_t k = 0; k < 4; ++k) {
+    mid_point += starts[k];
+    mid_point += ends[k];
+  }
+  return mid_point / 8.0;
+}
+
+/**
+ * @brief Finds the minimum and maximum extends of the given projection
+ * @details This projects each point of the given array of points using the
+ *   cameras view-projection matrix, and computes the minimum and maximum
+ *   of the projected points.
+ *
+ * @param min_extent Will store the minimum extent of the projected points in NDC space
+ * @param max_extent Will store the maximum extent of the projected points in NDC space
+ * @param transform The transformation matrix of the camera
+ * @param proj_points The array of points to project
+ * @param cam The camera to be used to project the points
+ */
+void find_min_max_extents(LVecBase3 &min_extent, LVecBase3 &max_extent, const LMatrix4 &transform, LVecBase3 const (&proj_points)[8], Camera *cam) {
+
+  min_extent.fill(1e10);
+  max_extent.fill(-1e10);
+  LPoint2 screen_points[8];
+
+  // Now project all points to the screen space of the current camera and also
+  // find the minimum and maximum extents
+  for (size_t k = 0; k < 8; ++k) {
+    LVecBase4 point(proj_points[k], 1);
+    LPoint4 proj_point = transform.xform(point);
+    LPoint3 proj_point_3d(proj_point.get_x(), proj_point.get_y(), proj_point.get_z());
+    cam->get_lens()->project(proj_point_3d, screen_points[k]);
+
+    // Find min / max extents
+    if (screen_points[k].get_x() > max_extent.get_x()) max_extent.set_x(screen_points[k].get_x());
+    if (screen_points[k].get_y() > max_extent.get_y()) max_extent.set_y(screen_points[k].get_y());
+
+    if (screen_points[k].get_x() < min_extent.get_x()) min_extent.set_x(screen_points[k].get_x());
+    if (screen_points[k].get_y() < min_extent.get_y()) min_extent.set_y(screen_points[k].get_y());
+
+    // Find min / max projected depth to adjust far plane
+    if (proj_point.get_y() > max_extent.get_z()) max_extent.set_z(proj_point.get_y());
+    if (proj_point.get_y() < min_extent.get_z()) min_extent.set_z(proj_point.get_y());
+  }
+}
+
+/**
+ * @brief Computes a film size from a given minimum and maximum extend
+ * @details This takes a minimum and maximum extent in NDC space and computes
+ *   the film size and film offset needed to cover that extent.
+ *
+ * @param film_size Output film size, can be used for Lens::set_film_size
+ * @param film_offset Output film offset, can be used for Lens::set_film_offset
+ * @param min_extent Minimum extent
+ * @param max_extent Maximum extent
+ */
+inline void get_film_properties(LVecBase2 &film_size, LVecBase2 &film_offset, const LVecBase3 &min_extent, const LVecBase3 &max_extent) {
+  float x_center = (min_extent.get_x() + max_extent.get_x()) * 0.5;
+  float y_center = (min_extent.get_y() + max_extent.get_y()) * 0.5;
+  float x_size = max_extent.get_x() - x_center;
+  float y_size = max_extent.get_y() - y_center;
+  film_size.set(x_size, y_size);
+  film_offset.set(x_center * 0.5, y_center * 0.5);
+}
+
+/**
+ * @brief Merges two arrays
+ * @details This takes two arrays which each 4 members and produces an array
+ *   with both arrays contained.
+ *
+ * @param dest Destination array
+ * @param array1 First array
+ * @param array2 Second array
+ */
+inline void merge_points_interleaved(LVecBase3 (&dest)[8], LVecBase3 const (&array1)[4], LVecBase3 const (&array2)[4]) {
+  for (size_t k = 0; k < 4; ++k) {
+    dest[k] = array1[k];
+    dest[k+4] = array2[k];
+  }
+}
+
+
+/**
+ * @brief Internal method to compute the splits
+ * @details This is the internal update method to update the PSSM splits.
+ *   It distributes the camera splits over the frustum, and updates the
+ *   MVP array aswell as the nearfar array.
+ *
+ * @param transform Main camera transform
+ * @param max_distance Maximum pssm distance, relative to the camera far plane
+ * @param light_vector Sun-Vector
+ */
+void PSSMCameraRig::compute_pssm_splits(const LMatrix4& transform, float max_distance, const LVecBase3& light_vector) {
+  nassertv(!_parent.is_empty());
+
+  // PSSM Distance should never be smaller than camera far plane.
+  nassertv(max_distance <= 1.0);
+
+  float filmsize_bias = 1.0 + _border_bias;
+
+  // Compute the positions of all cameras
+  for (size_t i = 0; i < _cam_nodes.size(); ++i) {
+    float split_start = get_split_start(i) * max_distance;
+    float split_end = get_split_start(i + 1) * max_distance;
+
+    LVecBase3 start_points[4];
+    LVecBase3 end_points[4];
+    LVecBase3 proj_points[8];
+
+    // Get split bounding box, and collect all points which define the frustum
+    for (size_t k = 0; k < 4; ++k) {
+      start_points[k] = get_interpolated_point((CoordinateOrigin)k, split_start);
+      end_points[k] = get_interpolated_point((CoordinateOrigin)k, split_end);
+      proj_points[k] = start_points[k];
+      proj_points[k + 4] = end_points[k];
+    }
+
+    // Compute approximate split mid point
+    LPoint3 split_mid = get_average_of_points(start_points, end_points);
+    LPoint3 cam_start = split_mid + light_vector * _sun_distance;
+
+    // Reset the film size, offset and far-plane
+    Camera* cam = DCAST(Camera, _cam_nodes[i].node());
+    cam->get_lens()->set_film_size(1, 1);
+    cam->get_lens()->set_film_offset(0, 0);
+    cam->get_lens()->set_near_far(1, 100);
+
+    // Find a good initial position
+    _cam_nodes[i].set_pos(cam_start);
+    _cam_nodes[i].look_at(split_mid);
+
+    LVecBase3 best_min_extent, best_max_extent;
+
+    // Find minimum and maximum extents of the points
+    LMatrix4 merged_transform = _parent.get_transform(_cam_nodes[i])->get_mat();
+    find_min_max_extents(best_min_extent, best_max_extent, merged_transform, proj_points, cam);
+
+    // Find the film size to cover all points
+    LVecBase2 film_size, film_offset;
+    get_film_properties(film_size, film_offset, best_min_extent, best_max_extent);
+
+    if (_use_fixed_film_size) {
+      // In case we use a fixed film size, store the maximum film size, and
+      // only change the film size if a new maximum is there
+      if (_max_film_sizes[i].get_x() < film_size.get_x()) _max_film_sizes[i].set_x(film_size.get_x());
+      if (_max_film_sizes[i].get_y() < film_size.get_y()) _max_film_sizes[i].set_y(film_size.get_y());
+
+      cam->get_lens()->set_film_size(_max_film_sizes[i] * filmsize_bias);
+    } else {
+      // If we don't use a fixed film size, we can just set the film size
+      // on the lens.
+      cam->get_lens()->set_film_size(film_size * filmsize_bias);
+    }
+
+    // Compute new film offset
+    cam->get_lens()->set_film_offset(film_offset);
+    cam->get_lens()->set_near_far(10, best_max_extent.get_z());
+    _camera_nearfar[i] = LVecBase2(10, best_max_extent.get_z());
+
+    // Compute the camera MVP
+    LMatrix4 mvp = compute_mvp(i);
+
+    // Stable CSM Snapping
+    if (_use_stable_csm) {
+      LPoint3 snap_offset = get_snap_offset(mvp, _resolution);
+      _cam_nodes[i].set_pos(_cam_nodes[i].get_pos() + snap_offset);
+
+      // Compute the new mvp, since we changed the snap offset
+      mvp = compute_mvp(i);
+    }
+
+    _camera_mvps.set_element(i, mvp);
+  }
+}
+
+
+/**
+ * @brief Updates the PSSM camera rig
+ * @details This updates the rig with an updated camera position, and a given
+ *   light vector. This should be called on a per-frame basis. It will reposition
+ *   all camera sources to fit the frustum based on the pssm distribution.
+ *
+ *   The light vector should be the vector from the light source, not the
+ *   vector to the light source.
+ *
+ * @param cam_node Target camera node
+ * @param light_vector The vector from the light to any point
+ */
+void PSSMCameraRig::update(NodePath cam_node, const LVecBase3 &light_vector) {
+  nassertv(!cam_node.is_empty());
+  _update_collector.start();
+
+  // Get camera node transform
+  LMatrix4 transform = cam_node.get_transform()->get_mat();
+
+  // Get Camera and Lens pointers
+  Camera* cam = DCAST(Camera, cam_node.get_child(0).node());
+  nassertv(cam != nullptr);
+  Lens* lens = cam->get_lens();
+
+  // Extract near and far points:
+  lens->extrude(LPoint2(-1, 1),  _curr_near_points[UpperLeft],  _curr_far_points[UpperLeft]);
+  lens->extrude(LPoint2(1, 1),   _curr_near_points[UpperRight], _curr_far_points[UpperRight]);
+  lens->extrude(LPoint2(-1, -1), _curr_near_points[LowerLeft],  _curr_far_points[LowerLeft]);
+  lens->extrude(LPoint2(1, -1),  _curr_near_points[LowerRight], _curr_far_points[LowerRight]);
+
+  // Construct MVP to project points to world space
+  LMatrix4 mvp = transform * lens->get_view_mat();
+
+  // Project all points to world space
+  for (size_t i = 0; i < 4; ++i) {
+    LPoint4 ws_near = mvp.xform(_curr_near_points[i]);
+    LPoint4 ws_far = mvp.xform(_curr_far_points[i]);
+    _curr_near_points[i].set(ws_near.get_x(), ws_near.get_y(), ws_near.get_z());
+    _curr_far_points[i].set(ws_far.get_x(), ws_far.get_y(), ws_far.get_z());
+  }
+
+  // Do the actual PSSM
+  compute_pssm_splits( transform, _pssm_distance / lens->get_far(), light_vector );
+
+  _update_collector.stop();
+}
+

+ 125 - 0
contrib/src/rplight/pssmCameraRig.h

@@ -0,0 +1,125 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef PSSMCAMERARIG_H
+#define PSSMCAMERARIG_H
+
+#include "pandabase.h"
+#include "luse.h"
+#include "camera.h"
+#include "nodePath.h"
+#include "pStatCollector.h"
+
+#include <vector>
+
+/**
+ * @brief Main class used for handling PSSM
+ * @details This is the main class for supporting PSSM, it is used by the PSSM
+ *   plugin to compute the position of the splits.
+ *
+ *   It supports handling a varying amount of cameras, and fitting those cameras
+ *   into the main camera frustum, to render distant shadows. It also supports
+ *   various optimizations for fitting the frustum, e.g. rotating the sources
+ *   to get a better coverage.
+ *
+ *   It also provides methods to get arrays of data about the used cameras
+ *   view-projection matrices and their near and far plane, which is required for
+ *   processing the data in the shadow sampling shader.
+ *
+ *   In this class, there is often referred to "Splits" or also called "Cascades".
+ *   These denote the different cameras which are used to split the frustum,
+ *   and are a common term related to the PSSM algorithm.
+ *
+ *   To understand the functionality of this class, a detailed knowledge of the
+ *   PSSM algorithm is helpful.
+ */
+class PSSMCameraRig {
+PUBLISHED:
+  PSSMCameraRig(size_t num_splits);
+  ~PSSMCameraRig();
+
+  inline void set_pssm_distance(float distance);
+  inline void set_sun_distance(float distance);
+  inline void set_use_fixed_film_size(bool flag);
+  inline void set_resolution(size_t resolution);
+  inline void set_use_stable_csm(bool flag);
+  inline void set_logarithmic_factor(float factor);
+  inline void set_border_bias(float bias);
+
+  void update(NodePath cam_node, const LVecBase3 &light_vector);
+  inline void reset_film_size_cache();
+
+  inline NodePath get_camera(size_t index);
+
+  void reparent_to(NodePath parent);
+  inline const PTA_LMatrix4 &get_mvp_array();
+  inline const PTA_LVecBase2 &get_nearfar_array();
+
+public:
+  // Used to access the near and far points in the array
+  enum CoordinateOrigin {
+    UpperLeft = 0,
+    UpperRight,
+    LowerLeft,
+    LowerRight
+  };
+
+protected:
+  void init_cam_nodes();
+  void compute_pssm_splits(const LMatrix4& transform, float max_distance,
+               const LVecBase3 &light_vector);
+  inline float get_split_start(size_t split_index);
+  LMatrix4 compute_mvp(size_t cam_index);
+  inline LPoint3 get_interpolated_point(CoordinateOrigin origin, float depth);
+  LVecBase3 get_snap_offset(const LMatrix4& mat, size_t resolution);
+
+  vector<NodePath> _cam_nodes;
+  vector<Camera*> _cameras;
+  vector<LVecBase2> _max_film_sizes;
+
+  // Current near and far points
+  // Order: UL, UR, LL, LR (See CoordinateOrigin)
+  LPoint3 _curr_near_points[4];
+  LPoint3 _curr_far_points[4];
+  float _pssm_distance;
+  float _sun_distance;
+  float _logarithmic_factor;
+  float _border_bias;
+  bool _use_fixed_film_size;
+  bool _use_stable_csm;
+  size_t _resolution;
+  size_t _num_splits;
+  NodePath _parent;
+
+  PTA_LMatrix4 _camera_mvps;
+  PTA_LVecBase2 _camera_nearfar;
+
+  static PStatCollector _update_collector;
+};
+
+#include "pssmCameraRig.I"
+
+#endif // PSSMCAMERARIG_H

+ 406 - 0
contrib/src/rplight/rpLight.I

@@ -0,0 +1,406 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+/**
+ * @brief Returns the amount of shadow sources
+ * @details This returns the amount of shadow sources attached to this light.
+ *   In case the light has no shadows enabled, or the light was not attached
+ *   yet, this returns 0.
+ *
+ * @return Amount of shadow sources
+ */
+inline int RPLight::get_num_shadow_sources() const {
+  return _shadow_sources.size();
+}
+
+/**
+ * @brief Returns the n-th shadow source
+ * @details This returns the n-th attached shadow source. This ranges from
+ *   0 .. RPLight::get_num_shadow_sources(). If an invalid index is passed,
+ *   an assertion is thrown.
+ *
+ * @param index Index of the source
+ * @return Handle to the shadow source
+ */
+inline ShadowSource* RPLight::get_shadow_source(size_t index) const {
+  nassertr(index < _shadow_sources.size(), nullptr); // Invalid shadow source index
+  return _shadow_sources[index];
+}
+
+/**
+ * @brief Clears all shadow source
+ * @details This removes and destructs all shadow sources attached to this light.
+ *   This usually gets called when the light gets detached or destructed.
+ *   All shadows sources are freed, and then removed from the shadow source list.
+ */
+inline void RPLight::clear_shadow_sources() {
+  for (size_t i = 0; i < _shadow_sources.size(); ++i) {
+    delete _shadow_sources[i];
+  }
+  _shadow_sources.clear();
+}
+
+/**
+ * @brief Sets whether the light needs an update
+ * @details This controls whether the light needs to get an update. This is the
+ *   case when a property of the light changed, e.g. position or color. It does
+ *   not affect the shadows (For that use RPLight::invalidate_shadows()).
+ *   When this flag is set to true, the light will get resubmitted to the GPU
+ *   in the next update cycle.
+ *
+ *   You should usually never set the flag to false manually. The
+ *   InternalLightManager will do this when the data got sucessfully updated.
+ *
+ * @param flag Update-Flag
+ */
+inline void RPLight::set_needs_update(bool flag) {
+  _needs_update = flag;
+}
+
+/**
+ * @brief Returns whether the light needs an update
+ * @details This returns whether the light needs an update. This might be the
+ *   case when a property of the light was changed, e.g. position or color.
+ *   It does not affect the shadows, you have to query the update flag of each
+ *   individual source for that.
+ *   The return value is the value previously set with RPLight::set_needs_update.
+ *
+ * @return Update-flag
+ */
+inline bool RPLight::get_needs_update() const {
+  return _needs_update;
+}
+
+/**
+ * @brief Returns whether the light has a slot
+ * @details This returns wheter the light currently is attached, and thus has
+ *   a slot in the InternalLightManagers light list. When the light is attached,
+ *   this returns true, otherwise it will return false.
+ *
+ * @return true if the light has a slot, false otherwise
+ */
+inline bool RPLight::has_slot() const {
+  return _slot >= 0;
+}
+
+/**
+ * @brief Returns the slot of the light
+ * @details This returns the slot of the light. This is the space on the GPU
+ *   where the light is stored. If the light is not attached yet, this will
+ *   return -1, otherwise the index of the light.
+ *
+ * @return Light-Slot
+ */
+inline int RPLight::get_slot() const {
+  return _slot;
+}
+
+/**
+ * @brief Removes the light slot
+ * @details This is an internal method to remove the slot of the light. It gets
+ *   called by the InternalLightManager when a light gets detached. It internally
+ *   sets the slot to -1 to indicate the light is no longer attached.
+ */
+inline void RPLight::remove_slot() {
+  _slot = -1;
+}
+
+/**
+ * @brief Assigns a slot to the light
+ * @details This assigns a slot to the light, marking it as attached. The slot
+ *   relates to the index in the GPU's storage of lights. This is an internal
+ *   method called by the InternalLightManager when the light got attached.
+ *
+ * @param slot Slot of the light
+ */
+inline void RPLight::assign_slot(int slot) {
+  _slot = slot;
+}
+
+/**
+ * @brief Invalidates the shadows
+ * @details This invalidates all shadows of the light, causing them to get
+ *   regenerated. This might be the case  when the lights position or similar
+ *   changed. This will cause all shadow sources to be updated, emitting a
+ *   shadow update. Be careful when calling this method if you don't want all
+ *   sources to get updated. If you only have to invalidate a single shadow source,
+ *   use get_shadow_source(n)->set_needs_update(true).
+ */
+inline void RPLight::invalidate_shadows() {
+  for (size_t i = 0; i < _shadow_sources.size(); ++i) {
+    _shadow_sources[i]->set_needs_update(true);
+  }
+}
+
+/**
+ * @brief Sets the position of the light
+ * @details This sets the position of the light in world space. It will cause
+ *   the light to get invalidated, and resubmitted to the GPU.
+ *
+ * @param pos Position in world space
+ */
+inline void RPLight::set_pos(const LVecBase3 &pos) {
+  set_pos(pos.get_x(), pos.get_y(), pos.get_z());
+}
+
+/**
+ * @brief Sets the position of the light
+ * @details @copydetails RPLight::set_pos(const LVecBase3 &pos)
+ *
+ * @param x X-component of the position
+ * @param y Y-component of the position
+ * @param z Z-component of the position
+ */
+inline void RPLight::set_pos(float x, float y, float z) {
+  _position.set(x, y, z);
+  set_needs_update(true);
+  invalidate_shadows();
+}
+
+/**
+ * @brief Returns the position of the light
+ * @details This returns the position of the light previously set with
+ *   RPLight::set_pos(). The returned position is in world space.
+ * @return Light-position
+ */
+inline const LVecBase3& RPLight::get_pos() const {
+  return _position;
+}
+
+/**
+ * @brief Sets the lights color
+ * @details This sets the lights color. The color should not include the brightness
+ *   of the light, you should control that with the energy. The color specifies
+ *   the lights "tint" and will get multiplied with its specular and diffuse
+ *   contribution.
+ *
+ *   The color will be normalized by dividing by the colors luminance. Setting
+ *   higher values than 1.0 will have no effect.
+ *
+ * @param color Light color
+ */
+inline void RPLight::set_color(const LVecBase3 &color) {
+  _color = color;
+  _color /= 0.2126 * color.get_x() + 0.7152 * color.get_y() + 0.0722 * color.get_z();
+  set_needs_update(true);
+}
+
+/**
+ * @brief Sets the lights color
+ * @details @copydetails RPLight::set_color(const LVecBase3 &color)
+ *
+ * @param r Red-component of the color
+ * @param g Green-component of the color
+ * @param b Blue-component of the color
+ */
+inline void RPLight::set_color(float r, float g, float b) {
+  set_color(LVecBase3(r, g, b));
+}
+
+/**
+ * @brief Returns the lights color
+ * @details This returns the light color, previously set with RPLight::set_color.
+ *   This does not include the energy of the light. It might differ from what
+ *   was set with set_color, because the color is normalized by dividing it
+ *   by its luminance.
+ * @return Light-color
+ */
+inline const LVecBase3& RPLight::get_color() const {
+  return _color;
+}
+
+/**
+ * @brief Sets the energy of the light
+ * @details This sets the energy of the light, which can be seen as the brightness
+ *   of the light. It will get multiplied with the normalized color.
+ *
+ * @param energy energy of the light
+ */
+inline void RPLight::set_energy(float energy) {
+  _energy = energy;
+  set_needs_update(true);
+}
+
+/**
+ * @brief Returns the energy of the light
+ * @details This returns the energy of the light, previously set with
+ *   RPLight::set_energy.
+ *
+ * @return energy of the light
+ */
+inline float RPLight::get_energy() const {
+  return _energy;
+}
+
+/**
+ * @brief Returns the type of the light
+ * @details This returns the internal type of the light, which was specified
+ *   in the lights constructor. This can be used to distinguish between light
+ *   types.
+ * @return Type of the light
+ */
+inline RPLight::LightType RPLight::get_light_type() const {
+  return _light_type;
+}
+
+/**
+ * @brief Controls whether the light casts shadows
+ * @details This sets whether the light casts shadows. You can not change this
+ *   while the light is attached. When flag is set to true, the light will be
+ *   setup to cast shadows, spawning shadow sources based on the lights type.
+ *   If the flag is set to false, the light will be inddicated to cast no shadows.
+ *
+ * @param flag Whether the light casts shadows
+ */
+inline void RPLight::set_casts_shadows(bool flag) {
+  if (has_slot()) {
+    cerr << "Light is already attached, can not call set_casts_shadows!" << endl;
+    return;
+  }
+  _casts_shadows = flag;
+}
+
+/**
+ * @brief Returns whether the light casts shadows
+ * @details This returns whether the light casts shadows, the returned value
+ *   is the one previously set with RPLight::set_casts_shadows.
+ *
+ * @return true if the light casts shadows, false otherwise
+ */
+inline bool RPLight::get_casts_shadows() const {
+  return _casts_shadows;
+}
+
+/**
+ * @brief Sets the lights shadow map resolution
+ * @details This sets the lights shadow map resolution. This has no effect
+ *   when the light is not told to cast shadows (Use RPLight::set_casts_shadows).
+ *
+ *   When calling this on a light with multiple shadow sources (e.g. PointLight),
+ *   this controls the resolution of each source. If the light has 6 shadow sources,
+ *   and you use a resolution of 512x512, the lights shadow map will occur a
+ *   space of 6 * 512x512 maps in the shadow atlas.
+ *
+ * @param resolution Resolution of the shadow map in pixels
+ */
+inline void RPLight::set_shadow_map_resolution(size_t resolution) {
+  nassertv(resolution >= 32 && resolution <= 16384);
+  _source_resolution = resolution;
+  invalidate_shadows();
+}
+
+/**
+ * @brief Returns the shadow map resolution
+ * @details This returns the shadow map resolution of each source of the light.
+ *   If the light is not setup to cast shadows, this value is meaningless.
+ *   The returned value is the one previously set with RPLight::set_shadow_map_resolution.
+ *
+ * @return Shadow map resolution in pixels
+ */
+inline size_t RPLight::get_shadow_map_resolution() const {
+  return _source_resolution;
+}
+
+/**
+ * @brief Sets the ies profile
+ * @details This sets the ies profile of the light. The parameter should be a
+ *   handle previously returned by RenderPipeline.load_ies_profile. Using a
+ *   value of -1 indicates no ies profile.
+ *
+ *   Notice that for ies profiles which cover a whole range, you should use
+ *   PointLights, whereas for ies profiles which only cover the lower hemisphere
+ *   you should use SpotLights for the best performance.
+ *
+ * @param profile IES Profile handle
+ */
+inline void RPLight::set_ies_profile(int profile) {
+  _ies_profile = profile;
+  set_needs_update(true);
+}
+
+/**
+ * @brief Returns the lights ies profile
+ * @details This returns the ies profile of a light, previously set with
+ *   RPLight::set_ies_profile. In case no ies profile was set, returns -1.
+ *
+ * @return IES Profile handle
+ */
+inline int RPLight::get_ies_profile() const {
+  return _ies_profile;
+}
+
+/**
+ * @brief Returns whether the light has an ies profile assigned
+ * @details This returns whether the light has an ies profile assigned,
+ *   previously done with RPLight::set_ies_profile.
+ *
+ * @return true if the light has an ies profile assigned, false otherwise
+ */
+inline bool RPLight::has_ies_profile() const {
+  return _ies_profile >= 0;
+}
+
+/**
+ * @brief Clears the ies profile
+ * @details This clears the ies profile of the light, telling it to no longer
+ *   use an ies profile, and instead use the default attenuation.
+ */
+inline void RPLight::clear_ies_profile() {
+  set_ies_profile(-1);
+}
+
+/**
+ * @brief Sets the near plane of the light
+ * @details This sets the near plane of all shadow sources of the light. It has
+ *   no effects if the light does not cast shadows. This prevents artifacts from
+ *   objects near to the light. It behaves like Lens::set_near_plane.
+ *
+ *   It can also help increasing shadow map precision, low near planes will
+ *   cause the precision to suffer. Try setting the near plane as big as possible.
+ *
+ *   If a negative or zero near plane is passed, an assertion is thrown.
+ *
+ * @param near_plane Near-plane
+ */
+inline void RPLight::set_near_plane(float near_plane) {
+  nassertv(near_plane > 0.00001);
+  _near_plane = near_plane;
+  invalidate_shadows();
+}
+
+/**
+ * @brief Returns the near plane of the light
+ * @details This returns the lights near plane, previously set with
+ *   RPLight::set_near_plane. If the light does not cast shadows, this value
+ *   is meaningless.
+ *
+ * @return Near-plane
+ */
+inline float RPLight::get_near_plane() const {
+  return _near_plane;
+}
+

+ 137 - 0
contrib/src/rplight/rpLight.cxx

@@ -0,0 +1,137 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#include "rpLight.h"
+
+
+/**
+ * @brief Constructs a new light with the given type
+ * @details This constructs a new base light with the given light type.
+ *   Sub-Classes should call this to initialize all properties.
+ *
+ * @param light_type Type of the light
+ */
+RPLight::RPLight(LightType light_type) {
+  _light_type = light_type;
+  _needs_update = false;
+  _casts_shadows = false;
+  _slot = -1;
+  _position.fill(0);
+  _color.fill(1);
+  _ies_profile = -1;
+  _source_resolution = 512;
+  _near_plane = 0.5;
+  _energy = 20.0;
+}
+
+/**
+ * @brief Writes the light to a GPUCommand
+ * @details This writes all of the lights data to the given GPUCommand handle.
+ *   Subclasses should first call this method, and then append their own
+ *   data. This makes sure that for unpacking a light, no information about
+ *   the type of the light is required.
+ *
+ * @param cmd The GPUCommand to write to
+ */
+void RPLight::write_to_command(GPUCommand &cmd) {
+  cmd.push_int(_light_type);
+  cmd.push_int(_ies_profile);
+
+  if (_casts_shadows) {
+    // If we casts shadows, write the index of the first source, we expect
+    // them to be consecutive
+    nassertv(_shadow_sources.size() >= 0);
+    nassertv(_shadow_sources[0]->has_slot());
+    cmd.push_int(_shadow_sources[0]->get_slot());
+  } else {
+    // If we cast no shadows, just push a negative number
+    cmd.push_int(-1);
+  }
+
+  cmd.push_vec3(_position);
+
+  // Get the lights color by multiplying color with energy. Divide by
+  // 100, since 16bit floating point buffers only go up to 65000.0, which
+  // prevents very bright lights 
+  cmd.push_vec3(_color * _energy / 100.0);
+}
+
+/**
+ * @brief Light destructor
+ * @details This destructs the light, cleaning up all resourced used. The light
+ *   should be detached at this point, because while the Light is attached,
+ *   the InternalLightManager holds a reference to prevent it from being
+ *   destructed.
+ */
+RPLight::~RPLight() {
+  nassertv(!has_slot()); // Light still attached - should never happen
+  clear_shadow_sources();
+}
+
+/**
+ * @brief Sets the lights color from a given color temperature
+ * @details This sets the lights color, given a temperature. This is more
+ *   physically based than setting a user defined color. The color will be
+ *   computed from the given temperature.
+ *
+ * @param temperature Light temperature
+ */
+void RPLight::set_color_from_temperature(float temperature) {
+
+  // Thanks to rdb for this conversion script
+  float mm = 1000.0 / temperature;
+  float mm2 = mm * mm;
+  float mm3 = mm2 * mm;
+  float x, y;
+
+  if (temperature < 4000) {
+    x = -0.2661239 * mm3 - 0.2343580 * mm2 + 0.8776956 * mm + 0.179910;
+  } else {
+    x = -3.0258469 * mm3 + 2.1070379 * mm2 + 0.2226347 * mm + 0.240390;
+  }
+
+  float x2 = x * x;
+  float x3 = x2 * x;
+  if (temperature < 2222) {
+    y = -1.1063814 * x3 - 1.34811020 * x2 + 2.18555832 * x - 0.20219683;
+  } else if (temperature < 4000) {
+    y = -0.9549476 * x3 - 1.37418593 * x2 + 2.09137015 * x - 0.16748867;
+  } else {
+    y =  3.0817580 * x3 - 5.87338670 * x2 + 3.75112997 * x - 0.37001483;
+  }
+
+  // xyY to XYZ, assuming Y=1.
+  LVecBase3 xyz(x / y, 1, (1 - x - y) / y);
+
+  // Convert XYZ to linearized sRGB.
+  const static LMatrix3 xyz_to_rgb(
+   3.2406, -0.9689,  0.0557,
+  -1.5372,  1.8758, -0.2050,
+  -0.4986,  0.0415,  1.0570);
+
+  set_color(xyz_to_rgb.xform(xyz));
+}

+ 130 - 0
contrib/src/rplight/rpLight.h

@@ -0,0 +1,130 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef RPLIGHT_H
+#define RPLIGHT_H
+
+#include "referenceCount.h"
+#include "luse.h"
+#include "gpuCommand.h"
+#include "shadowSource.h"
+
+/**
+ * @brief Base class for Lights
+ * @details This is the base class for all lights in the render pipeline. It
+ *   stores common properties, and provides methods to modify these.
+ *   It also defines some interface functions which subclasses have to implement.
+ */
+class RPLight : public ReferenceCount {
+PUBLISHED:
+  /**
+   * Different types of light.
+   */
+  enum LightType {
+    LT_empty = 0,
+    LT_point_light = 1,
+    LT_spot_light = 2,
+  };
+
+public:
+  RPLight(LightType light_type);
+  virtual ~RPLight();
+
+  virtual void init_shadow_sources() = 0;
+  virtual void update_shadow_sources() = 0;
+  virtual void write_to_command(GPUCommand &cmd);
+
+  inline int get_num_shadow_sources() const;
+  inline ShadowSource* get_shadow_source(size_t index) const;
+  inline void clear_shadow_sources();
+
+  inline void set_needs_update(bool flag);
+  inline bool get_needs_update() const;
+
+  inline bool has_slot() const;
+  inline int get_slot() const;
+  inline void remove_slot();
+  inline void assign_slot(int slot);
+
+PUBLISHED:
+  inline void invalidate_shadows();
+
+  inline void set_pos(const LVecBase3 &pos);
+  inline void set_pos(float x, float y, float z);
+  inline const LVecBase3& get_pos() const;
+  MAKE_PROPERTY(pos, get_pos, set_pos);
+
+  inline void set_color(const LVecBase3 &color);
+  inline void set_color(float r, float g, float b);
+  inline const LVecBase3& get_color() const;
+  MAKE_PROPERTY(color, get_color, set_color);
+
+  void set_color_from_temperature(float temperature);
+
+  inline void set_energy(float energy);
+  inline float get_energy() const;
+  MAKE_PROPERTY(energy, get_energy, set_energy);
+
+  inline LightType get_light_type() const;
+  MAKE_PROPERTY(light_type, get_light_type);
+
+  inline void set_casts_shadows(bool flag = true);
+  inline bool get_casts_shadows() const;
+  MAKE_PROPERTY(casts_shadows, get_casts_shadows, set_casts_shadows);
+
+  inline void set_shadow_map_resolution(size_t resolution);
+  inline size_t get_shadow_map_resolution() const;
+  MAKE_PROPERTY(shadow_map_resolution, get_shadow_map_resolution, set_shadow_map_resolution);
+
+  inline void set_ies_profile(int profile);
+  inline int get_ies_profile() const;
+  inline bool has_ies_profile() const;
+  inline void clear_ies_profile();
+  MAKE_PROPERTY2(ies_profile, has_ies_profile, get_ies_profile,
+                 set_ies_profile, clear_ies_profile);
+
+  inline void set_near_plane(float near_plane);
+  inline float get_near_plane() const;
+  MAKE_PROPERTY(near_plane, get_near_plane, set_near_plane);
+
+protected:
+  int _slot;
+  int _ies_profile;
+  size_t _source_resolution;
+  bool _needs_update;
+  bool _casts_shadows;
+  LVecBase3 _position;
+  LVecBase3 _color;
+  float _energy;
+  LightType _light_type;
+  float _near_plane;
+
+  vector<ShadowSource*> _shadow_sources;
+};
+
+#include "rpLight.I"
+
+#endif // RP_LIGHT_H

+ 82 - 0
contrib/src/rplight/rpPointLight.I

@@ -0,0 +1,82 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+/**
+ * @brief Sets the radius of the light
+ * @details This sets the radius of the light. It controls the lights
+ *   influence. After a distance greater than this radius, the light influence
+ *   is zero.
+ *
+ * @param radius Light radius in world space
+ */
+inline void RPPointLight::set_radius(float radius) {
+  nassertv(radius > 0); // Invalid light radius
+  _radius = radius;
+  set_needs_update(true);
+  invalidate_shadows();
+}
+
+/**
+ * @brief Returns the lights radius
+ * @details This returns the lights radius previously set with
+ *   RPPointLight::set_radius
+ * @return Light radius in world space
+ */
+inline float RPPointLight::get_radius() const {
+  return _radius;
+}
+
+/**
+ * @brief Sets the inner radius of the light
+ * @details This sets the inner radius of the light. Anything greater than
+ *   zero causes the light to get an area light. This has influence on the
+ *   specular highlights of the light aswell as the shadows.
+ *
+ *   The inner radius controls the size of the lights sphere size in world
+ *   space units. A radius of 0 means the light has no inner radius, and the
+ *   light will be have like an infinite small point light source.
+ *   A radius greater than zero will cause the light to behave like it would be
+ *   an emissive sphere with the given inner radius emitting light. This is
+ *   more physically correct.
+ *
+ * @param inner_radius Inner-radius in world space
+ */
+inline void RPPointLight::set_inner_radius(float inner_radius) {
+  nassertv(inner_radius >= 0.01); // Invalid inner radius
+  _inner_radius = inner_radius;
+  set_needs_update(true);
+}
+
+/**
+ * @brief Returns the inner radius of the light
+ * @details This returns the inner radius of the light, previously set with
+ *   RPPointLight::get_inner_radius.
+ * @return [description]
+ */
+inline float RPPointLight::get_inner_radius() const {
+  return _inner_radius;
+}

+ 90 - 0
contrib/src/rplight/rpPointLight.cxx

@@ -0,0 +1,90 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#include "rpPointLight.h"
+
+
+/**
+ * @brief Constructs a new point light
+ * @details This contructs a new point light with default settings. By default
+ *   the light is set to be an infinitely small point light source. You can
+ *   change this with RPPointLight::set_inner_radius.
+ */
+RPPointLight::RPPointLight() : RPLight(RPLight::LT_point_light) {
+  _radius = 10.0;
+  _inner_radius = 0.01;
+}
+
+/**
+ * @brief Writes the light to a GPUCommand
+ * @details This writes the point light data to a GPUCommand.
+ * @see RPLight::write_to_command
+ *
+ * @param cmd The target GPUCommand
+ */
+void RPPointLight::write_to_command(GPUCommand &cmd) {
+  RPLight::write_to_command(cmd);
+  cmd.push_float(_radius);
+  cmd.push_float(_inner_radius);
+}
+
+/**
+ * @brief Inits the shadow sources of the light
+ * @details This inits all required shadow sources for the point light.
+ * @see RPLight::init_shadow_sources
+ */
+void RPPointLight::init_shadow_sources() {
+  nassertv(_shadow_sources.size() == 0);
+  // Create 6 shadow sources, one for each direction
+  for(size_t i = 0; i < 6; ++i) {
+    _shadow_sources.push_back(new ShadowSource());
+  }
+}
+
+/**
+ * @brief Updates the shadow sources
+ * @details This updates all shadow sources of the light.
+ * @see RPLight::update_shadow_sources
+ */
+void RPPointLight::update_shadow_sources() {
+  LVecBase3 directions[6] = {
+    LVecBase3( 1,  0,  0),
+    LVecBase3(-1,  0,  0),
+    LVecBase3( 0,  1,  0),
+    LVecBase3( 0, -1,  0),
+    LVecBase3( 0,  0,  1),
+    LVecBase3( 0,  0, -1)
+  };
+
+  // Increase fov to prevent artifacts at the shadow map transitions
+  const float fov = 90.0f + 3.0f;
+  for (size_t i = 0; i < _shadow_sources.size(); ++i) {
+    _shadow_sources[i]->set_resolution(get_shadow_map_resolution());
+    _shadow_sources[i]->set_perspective_lens(fov, _near_plane, _radius,
+                        _position, directions[i]);
+  }
+}

+ 63 - 0
contrib/src/rplight/rpPointLight.h

@@ -0,0 +1,63 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef RPPOINTLIGHT_H
+#define RPPOINTLIGHT_H
+
+#include "pandabase.h"
+#include "rpLight.h"
+
+/**
+ * @brief PointLight class
+ * @details This represents a point light, a light which has a position and
+ *   radius. Checkout the RenderPipeline documentation for more information
+ *   about this type of light.
+ */
+class RPPointLight : public RPLight {
+PUBLISHED:
+  RPPointLight();
+
+  inline void set_radius(float radius);
+  inline float get_radius() const;
+  MAKE_PROPERTY(radius, get_radius, set_radius);
+
+  inline void set_inner_radius(float inner_radius);
+  inline float get_inner_radius() const;
+  MAKE_PROPERTY(inner_radius, get_inner_radius, set_inner_radius);
+
+public:
+  virtual void write_to_command(GPUCommand &cmd);
+  virtual void update_shadow_sources();
+  virtual void init_shadow_sources();
+
+protected:
+  float _radius;
+  float _inner_radius;
+};
+
+#include "rpPointLight.I"
+
+#endif // RPPOINTLIGHT_H

+ 74 - 0
contrib/src/rplight/rpSpotLight.I

@@ -0,0 +1,74 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+
+inline void RPSpotLight::set_radius(float radius) {
+  _radius = radius;
+  set_needs_update(true);
+  invalidate_shadows();
+}
+
+inline float RPSpotLight::get_radius() const {
+  return _radius;
+}
+
+
+inline void RPSpotLight::set_fov(float fov) {
+  _fov = fov;
+  set_needs_update(true);
+  invalidate_shadows();
+}
+
+inline float RPSpotLight::get_fov() const {
+  return _fov;
+}
+
+inline void RPSpotLight::set_direction(LVecBase3 direction) {
+  _direction = direction;
+  _direction.normalize();
+  set_needs_update(true);
+  invalidate_shadows();
+}
+
+inline void RPSpotLight::set_direction(float dx, float dy, float dz) {
+  _direction.set(dx, dy, dz);
+  _direction.normalize();
+  set_needs_update(true);
+  invalidate_shadows();
+}
+
+inline const LVecBase3& RPSpotLight::get_direction() const {
+  return _direction;
+}
+
+inline void RPSpotLight::look_at(LVecBase3 point) {
+  set_direction(point - _position);
+}
+
+inline void RPSpotLight::look_at(float x, float y, float z) {
+  set_direction(LVecBase3(x, y, z) - _position);
+}

+ 80 - 0
contrib/src/rplight/rpSpotLight.cxx

@@ -0,0 +1,80 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#include "rpSpotLight.h"
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+
+
+/**
+ * @brief Creates a new spot light
+ * @details This creates a new spot light with default properties set. You should
+ *   set at least a direction, fov, radius and position to make the light useful.
+ */
+RPSpotLight::RPSpotLight() : RPLight(RPLight::LT_spot_light) {
+  _radius = 10.0;
+  _fov = 45.0;
+  _direction.set(0, 0, -1);
+}
+
+/**
+ * @brief Writes the light to a GPUCommand
+ * @details This writes the spot light data to a GPUCommand.
+ * @see RPLight::write_to_command
+ *
+ * @param cmd The target GPUCommand
+ */
+void RPSpotLight::write_to_command(GPUCommand &cmd) {
+  RPLight::write_to_command(cmd);
+  cmd.push_float(_radius);
+
+  // Encode FOV as cos(fov)
+  cmd.push_float(cos(_fov / 360.0 * M_PI));
+  cmd.push_vec3(_direction);
+}
+
+/**
+ * @brief Inits the shadow sources of the light
+ * @details This inits all required shadow sources for the spot light.
+ * @see RPLight::init_shadow_sources
+ */
+void RPSpotLight::init_shadow_sources() {
+  nassertv(_shadow_sources.size() == 0);
+  _shadow_sources.push_back(new ShadowSource());
+}
+
+/**
+ * @brief Updates the shadow sources
+ * @details This updates all shadow sources of the light.
+ * @see RPLight::update_shadow_sources
+ */
+void RPSpotLight::update_shadow_sources() {
+  _shadow_sources[0]->set_resolution(get_shadow_map_resolution());
+  _shadow_sources[0]->set_perspective_lens(_fov, _near_plane, _radius, _position, _direction);
+}
+

+ 71 - 0
contrib/src/rplight/rpSpotLight.h

@@ -0,0 +1,71 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef RPSPOTLIGHT_H
+#define RPSPOTLIGHT_H
+
+#include "pandabase.h"
+#include "rpLight.h"
+
+/**
+ * @brief SpotLight class
+ * @details This represents a spot light, a light which has a position, radius,
+ *   direction and FoV. Checkout the RenderPipeline documentation for more
+ *   information about this type of light.
+ */
+class RPSpotLight : public RPLight {
+PUBLISHED:
+  RPSpotLight();
+
+  inline void set_radius(float radius);
+  inline float get_radius() const;
+  MAKE_PROPERTY(radius, get_radius, set_radius);
+
+  inline void set_fov(float fov);
+  inline float get_fov() const;
+  MAKE_PROPERTY(fov, get_fov, set_fov);
+
+  inline void set_direction(LVecBase3 direction);
+  inline void set_direction(float dx, float dy, float dz);
+  inline const LVecBase3& get_direction() const;
+  inline void look_at(LVecBase3 point);
+  inline void look_at(float x, float y, float z);
+  MAKE_PROPERTY(direction, get_direction, set_direction);
+
+public:
+  virtual void write_to_command(GPUCommand &cmd);
+  virtual void init_shadow_sources();
+  virtual void update_shadow_sources();
+
+protected:
+  float _radius;
+  float _fov;
+  LVecBase3 _direction;
+};
+
+#include "rpSpotLight.I"
+
+#endif // RPSPOTLIGHT_H

+ 159 - 0
contrib/src/rplight/shadowAtlas.I

@@ -0,0 +1,159 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+/**
+ * @brief Returns the tile size of the atlas.
+ * @details This returns the tile size of the atlas, which was set in the
+ *   constructor. This is the smalles unit of the atlas, and every resolution
+ *   has to be a multiple of the tile size.
+ * @return Tile size in pixels
+ */
+inline int ShadowAtlas::get_tile_size() const {
+  return _tile_size;
+}
+
+/**
+ * @brief Sets a specific tile status.
+ * @details This marks a tile to either reserved or free, depending on the flag.
+ *   If flag is true, the tile gets marked as reserved. If flag is false, the
+ *   tile gets marked as free.
+ *
+ *   No bounds checking is done for performance reasons. Passing an invalid tile
+ *   index causes a crash. The coordinates are expected to be in tile space.
+ *
+ * @param x x-position of the tile
+ * @param y y-position of the tile
+ * @param flag Flag to set the tile to
+ */
+inline void ShadowAtlas::set_tile(size_t x, size_t y, bool flag) {
+  _flags[x + y * _num_tiles] = flag;
+}
+
+/**
+ * @brief Returns the status of a given tile.
+ * @details This returns the value of a tile. If the tile is true, this means
+ *   the tile is already reserved. If the tile is false, the tile can be
+ *   used, and is not reserved.
+ *
+ *   No bounds checking is done for performance reasons. Passing an invalid tile
+ *   index causes a crash. The coordinates are expected to be in tile space.
+ *
+ * @param x x-position of the tile
+ * @param y y-position of the tile
+ *
+ * @return Tile-Status
+ */
+inline bool ShadowAtlas::get_tile(size_t x, size_t y) const {
+  return _flags[x + y * _num_tiles];
+}
+
+/**
+ * @brief Checks wheter a given region is free.
+ * @details This checks whether a given region in the atlas is still free. This
+ *   is true if *all* tiles in that region are false, and thus are not taken yet.
+ *   The coordinates are expected to be in tile space.
+ *
+ *   Passing an invalid region, causes an assertion, in case those are enabled.
+ *   If assertions are optimized out, this method will crash when passing invalid
+ *   bounds.
+ *
+ * @param x x- start position of the region
+ * @param y y- start position of the region
+ * @param w width of the region
+ * @param h height of the region
+ * @return true if the region is completely free, else false
+ */
+inline bool ShadowAtlas::region_is_free(size_t x, size_t y, size_t w, size_t h) const {
+  // Check if we are out of bounds, this should be disabled for performance
+  // reasons at some point.
+  nassertr(x >= 0 && y >= 0 && x + w <= _num_tiles && y + h <= _num_tiles, false);
+
+  // Iterate over every tile in that region and check if it is still free.
+  for (size_t cx = 0; cx < w; ++cx) {
+    for (size_t cy = 0; cy < h; ++cy) {
+      if (get_tile(cx + x, cy + y)) return false;
+    }
+  }
+  return true;
+}
+
+/**
+ * @brief Returns the amount of tiles required to store a resolution.
+ * @details Returns the amount of tiles which would be required to store a
+ *   given resolution. This basically just returns resolution / tile_size.
+ *
+ *   When an invalid resolution is passed (not a multiple of the tile size),
+ *   an error is printed and 1 is returned.
+ *   When a negative or zero resolution is passed, undefined behaviour occurs.
+ *
+ * @param resolution The resolution to compute the amount of tiles for
+ * @return Amount of tiles to store the resolution
+ */
+inline int ShadowAtlas::get_required_tiles(size_t resolution) const {
+  nassertr(resolution > 0, -1);
+
+  if (resolution % _tile_size != 0) {
+    shadowatlas_cat.error() << "Resolution " << resolution << " is not a multiple "
+                << "of the shadow atlas tile size (" << _tile_size << ")!" << endl;
+    return 1;
+  }
+  return resolution / _tile_size;
+}
+
+/**
+ * @brief Converts a tile-space region to uv space.
+ * @details This converts a region (presumably from ShadowAtlas::find_and_reserve_region)
+ *   to uv space (0 .. 1 range). This can be used in shaders, since they expect
+ *   floating point coordinates instead of integer coordinates.
+ *
+ * @param region tile-space region
+ * @return uv-space region
+ */
+inline LVecBase4 ShadowAtlas::region_to_uv(const LVecBase4i& region) {
+  LVecBase4 flt = LVecBase4(region.get_x(), region.get_y(), region.get_z(), region.get_w());
+  return flt * ((float)_tile_size / (float)_size);
+}
+
+/**
+ * @brief Returns the amount of used tiles
+ * @details Returns the amount of used tiles in the atlas
+ * @return Amount of used tiles
+ */
+inline int ShadowAtlas::get_num_used_tiles() const {
+  return _num_used_tiles;
+}
+
+/**
+ * @brief Returns the amount of used tiles in percentage
+ * @details This returns in percentage from 0 to 1 how much space of the atlas
+ *   is used right now. A value of 1 means the atlas is completely full, whereas
+ *   a value of 0 means the atlas is completely free.
+ * @return Atlas usage in percentage
+ */
+inline float ShadowAtlas::get_coverage() const {
+  return float(_num_used_tiles) / float(_num_tiles * _num_tiles);
+}

+ 186 - 0
contrib/src/rplight/shadowAtlas.cxx

@@ -0,0 +1,186 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#include "shadowAtlas.h"
+#include <string.h>
+
+NotifyCategoryDef(shadowatlas, "");
+
+/**
+ * @brief Constructs a new shadow atlas.
+ * @details This constructs a new shadow atlas with the given size and tile size.
+ *
+ *   The size determines the total size of the atlas in pixels. It should be a
+ *   power-of-two to favour the GPU.
+ *
+ *   The tile_size determines the smallest unit of tiles the atlas can store.
+ *   If, for example, a tile_size of 32 is used, then every entry stored must
+ *   have a resolution of 32 or greater, and the resolution must be a multiple
+ *   of 32. This is to optimize the search in the atlas, so the atlas does not
+ *   have to check every pixel, and instead can just check whole tiles.
+ *
+ *   If you want to disable the use of tiles, set the tile_size to 1, which
+ *   will make the shadow atlas use pixels instead of tiles.
+ *
+ * @param size Atlas-size in pixels
+ * @param tile_size tile-size in pixels, or 1 to use no tiles.
+ */
+ShadowAtlas::ShadowAtlas(size_t size, size_t tile_size) {
+  nassertv(size > 1 && tile_size >= 1);
+  nassertv(tile_size < size && size % tile_size == 0);
+  _size = size;
+  _tile_size = tile_size;
+  _num_used_tiles = 0;
+  init_tiles();
+}
+
+/**
+ * @brief Destructs the shadow atlas.
+ * @details This destructs the shadow atlas, freeing all used resources.
+ */
+ShadowAtlas::~ShadowAtlas() {
+  delete [] _flags;
+}
+
+/**
+ * @brief Internal method to init the storage.
+ * @details This method setups the storage used for storing the tile flags.
+ */
+void ShadowAtlas::init_tiles() {
+  _num_tiles = _size / _tile_size;
+  _flags = new bool[_num_tiles * _num_tiles];
+  memset(_flags, 0x0, sizeof(bool) * _num_tiles * _num_tiles);
+}
+
+/**
+ * @brief Internal method to reserve a region in the atlas.
+ * @details This reserves a given region in the shadow atlas. The region should
+ *   be in tile space.This is called by the ShadowAtlas::find_and_reserve_region.
+ *   It sets all flags in that region to true, indicating that those are used.
+ *   When an invalid region is passed, an assertion is triggered. If assertions
+ *   are optimized out, undefined behaviour occurs.
+ *
+ * @param x x- start positition of the region
+ * @param y y- start position of the region
+ * @param w width of the region
+ * @param h height of the region
+ */
+void ShadowAtlas::reserve_region(size_t x, size_t y, size_t w, size_t h) {
+  // Check if we are out of bounds, this should be disabled for performance
+  // reasons at some point.
+  nassertv(x >= 0 && y >= 0 && x + w <= _num_tiles && y + h <= _num_tiles);
+
+  _num_used_tiles += w * h;
+
+  // Iterate over every tile in the region and mark it as used
+  for (size_t cx = 0; cx < w; ++cx) {
+    for (size_t cy = 0; cy < h; ++cy) {
+      set_tile(cx + x, cy + y, true);
+    }
+  }
+}
+
+/**
+ * @brief Finds space for a map of the given size in the atlas.
+ * @details This methods searches for a space to store a region of the given
+ *   size in the atlas. tile_width and tile_height should be already in tile
+ *   space. They can be converted using ShadowAtlas::get_required_tiles.
+ *
+ *   If no region is found, or an invalid size is passed, an integer vector with
+ *   all components set to -1 is returned.
+ *
+ *  If a region is found, an integer vector with the given layout is returned:
+ *   x: x- Start of the region
+ *   y: y- Start of the region
+ *   z: width of the region
+ *   w: height of the region
+ *
+ *   The layout is in tile space, and can get converted to uv space using
+ *   ShadowAtlas::region_to_uv.
+ *
+ * @param tile_width Width of the region in tile space
+ * @param tile_height Height of the region in tile space
+ *
+ * @return Region, see description, or -1 when no region is found.
+ */
+LVecBase4i ShadowAtlas::find_and_reserve_region(size_t tile_width, size_t tile_height) {
+
+  // Check for empty region
+  if (tile_width < 1 || tile_height < 1) {
+    shadowatlas_cat.error() << "Called find_and_reserve_region with null-region!" << endl;
+    return LVecBase4i(-1);
+  }
+
+  // Check for region bigger than the shadow atlas
+  if (tile_width > _num_tiles || tile_height > _num_tiles) {
+    shadowatlas_cat.error() << "Requested region exceeds shadow atlas size!" << endl;
+    return LVecBase4i(-1);
+  }
+
+  // Iterate over every possible region and check if its still free
+  for (size_t x = 0; x <= _num_tiles - tile_width; ++x) {
+    for (size_t y = 0; y <= _num_tiles - tile_height; ++y) {
+      if (region_is_free(x, y, tile_width, tile_height)) {
+        // Found free region, now reserve it
+        reserve_region(x, y, tile_width, tile_height);
+        return LVecBase4i(x, y, tile_width, tile_height);
+      }
+    }
+  }
+
+  // When we reached this part, we couldn't find a free region, so the atlas
+  // seems to be full.
+  shadowatlas_cat.error() << "Failed to find a free region of size " << tile_width
+              << " x " << tile_height << "!"  << endl;
+  return LVecBase4i(-1);
+}
+
+/**
+ * @brief Frees a given region
+ * @details This frees a given region, marking it as free so that other shadow
+ *   maps can use the space again. The region should be the same as returned
+ *   by ShadowAtlas::find_and_reserve_region.
+ *
+ *   If an invalid region is passed, an assertion is triggered. If assertions
+ *   are compiled out, undefined behaviour will occur.
+ *
+ * @param region Region to free
+ */
+void ShadowAtlas::free_region(const LVecBase4i& region) {
+  // Out of bounds check, can't hurt
+  nassertv(region.get_x() >= 0 && region.get_y() >= 0);
+  nassertv(region.get_x() + region.get_z() <= _num_tiles && region.get_y() + region.get_w() <= _num_tiles);
+
+  _num_used_tiles -= region.get_z() * region.get_w();
+
+  for (size_t x = 0; x < region.get_z(); ++x) {
+    for (size_t y = 0; y < region.get_w(); ++y) {
+      // Could do an assert here, that the tile should have been used (=true) before
+      set_tile(region.get_x() + x, region.get_y() + y, false);
+    }
+  }
+}

+ 80 - 0
contrib/src/rplight/shadowAtlas.h

@@ -0,0 +1,80 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef SHADOWATLAS_H
+#define SHADOWATLAS_H
+
+#include "pandabase.h"
+#include "lvecBase4.h"
+
+NotifyCategoryDecl(shadowatlas, EXPORT_CLASS, EXPORT_TEMPL);
+
+
+/**
+ * @brief Class which manages distributing shadow maps in an atlas.
+ * @details This class manages the shadow atlas. It handles finding and reserving
+ *   space for new shadow maps.
+ */
+class ShadowAtlas {
+PUBLISHED:
+  ShadowAtlas(size_t size, size_t tile_size = 32);
+  ~ShadowAtlas();
+
+  inline int get_num_used_tiles() const;
+  inline float get_coverage() const;
+
+  MAKE_PROPERTY(num_used_tiles, get_num_used_tiles);
+  MAKE_PROPERTY(coverage, get_coverage);
+
+public:
+
+  LVecBase4i find_and_reserve_region(size_t tile_width, size_t tile_height);
+  void free_region(const LVecBase4i& region);
+  inline LVecBase4 region_to_uv(const LVecBase4i& region);
+
+  inline int get_tile_size() const;
+  inline int get_required_tiles(size_t resolution) const;
+
+protected:
+
+  void init_tiles();
+
+  inline void set_tile(size_t x, size_t y, bool flag);
+  inline bool get_tile(size_t x, size_t y) const;
+
+  inline bool region_is_free(size_t x, size_t y, size_t w, size_t h) const;
+  void reserve_region(size_t x, size_t y, size_t w, size_t h);
+
+  size_t _size;
+  size_t _num_tiles;
+  size_t _tile_size;
+  size_t _num_used_tiles;
+  bool* _flags;
+};
+
+#include "shadowAtlas.I"
+
+#endif // SHADOWATLAS_H

+ 192 - 0
contrib/src/rplight/shadowManager.I

@@ -0,0 +1,192 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+/**
+ * @brief Sets the maximum amount of updates per frame.
+ * @details This controls the maximum amount of updated ShadowSources per frame.
+ *   The ShadowManager will take the first <max_updates> ShadowSources, and
+ *   generate shadow maps for them every frame. If there are more ShadowSources
+ *   waiting to get updated than available updates, the sources are sorted by
+ *   priority, and the update of the less important sources is delayed to the
+ *   next frame.
+ *
+ *   If the update count is set too low, and there are a lot of ShadowSources
+ *   waiting to get updated, artifacts will occur, and there might be ShadowSources
+ *   which never get updated, due to low priority.
+ *
+ *   If an update count of 0 is passed, no updates will happen. This also means
+ *   that there are no shadows. This is not recommended.
+ *
+ *   If an update count < 0 is passed, undefined behaviour occurs.
+ *
+ *   This method has to get called before ShadowManager::init, otherwise an
+ *   assertion will get triggered.
+ *
+ * @param max_updates Maximum amoumt of updates
+ */
+inline void ShadowManager::set_max_updates(size_t max_updates) {
+  nassertv(max_updates >= 0);
+  nassertv(_atlas == nullptr);  // ShadowManager was already initialized
+  if (max_updates == 0) {
+    shadowmanager_cat.warning() << "max_updates set to 0, no shadows will be updated." << endl;
+  }
+  _max_updates = max_updates;
+}
+
+/**
+ * @brief Sets the shadow atlas size
+ * @details This sets the desired shadow atlas size. It should be big enough
+ *   to store all important shadow sources, with some buffer, because the shadow
+ *   maps usually won't be fitted perfectly, so gaps can occur.
+ *
+ *   This has to get called before calling ShadowManager::init. When calling this
+ *   method after initialization, an assertion will get triggered.
+ *
+ * @param atlas_size Size of the shadow atlas in pixels
+ */
+inline void ShadowManager::set_atlas_size(size_t atlas_size) {
+  nassertv(atlas_size >= 16 && atlas_size <= 16384);
+  nassertv(_atlas == nullptr);  // ShadowManager was already initialized
+  _atlas_size = atlas_size;
+}
+
+/**
+ * @brief Returns the shadow atlas size.
+ * @details This returns the shadow atlas size previously set with
+ *   ShadowManager::set_atlas_size.
+ * @return Shadow atlas size in pixels
+ */
+inline size_t ShadowManager::get_atlas_size() const {
+  return _atlas_size;
+}
+
+
+/**
+ * @brief Returns a handle to the shadow atlas.
+ * @details This returns a handle to the internal shadow atlas instance. This
+ *   is only valid after calling ShadowManager::init. Calling this earlier will
+ *   trigger an assertion and undefined behaviour.
+ * @return The internal ShadowAtlas instance
+ */
+inline ShadowAtlas* ShadowManager::get_atlas() const {
+  nassertr(_atlas != nullptr, nullptr); // Can't hurt to check
+  return _atlas;
+}
+
+/**
+ * @brief Sets the target scene
+ * @details This sets the target scene for rendering shadows. All shadow cameras
+ *   will be parented to this scene to render shadows.
+ *
+ *   Usually the scene will be ShowBase.render. If the scene is an empty or
+ *   invalid NodePath, an assertion will be triggered.
+ *
+ *   This method has to get called before calling ShadowManager::init, or an
+ *   assertion will get triggered.
+ *
+ * @param scene_parent The target scene
+ */
+inline void ShadowManager::set_scene(NodePath scene_parent) {
+  nassertv(!scene_parent.is_empty());
+  nassertv(_atlas == nullptr);  // ShadowManager was already initialized
+  _scene_parent = scene_parent;
+}
+
+/**
+ * @brief Sets the handle to the TagStageManager.
+ * @details This sets the handle to the TagStateManager used by the pipeline.
+ *   Usually this is RenderPipeline.get_tag_mgr().
+ *
+ *   This has to get called before ShadowManager::init, otherwise an assertion
+ *   will get triggered.
+ *
+ * @param tag_mgr [description]
+ */
+inline void ShadowManager::set_tag_state_manager(TagStateManager* tag_mgr) {
+  nassertv(tag_mgr != nullptr);
+  nassertv(_atlas == nullptr);  // ShadowManager was already initialized
+  _tag_state_mgr = tag_mgr;
+}
+
+/**
+ * @brief Sets the handle to the Shadow targets output
+ * @details This sets the handle to the GraphicsOutput of the shadow atlas.
+ *   Usually this is RenderTarget.get_internal_buffer(), whereas the RenderTarget
+ *   is the target of the ShadowStage.
+ *
+ *   This is used for creating display regions and attaching cameras to them,
+ *   for performing shadow updates.
+ *
+ *   This has to get called before ShadowManager::init, otherwise an assertion
+ *   will be triggered.
+ *
+ * @param graphics_output [description]
+ */
+inline void ShadowManager::set_atlas_graphics_output(GraphicsOutput* graphics_output) {
+  nassertv(graphics_output != nullptr);
+  nassertv(_atlas == nullptr);  // ShadowManager was already initialized
+  _atlas_graphics_output = graphics_output;
+}
+
+
+/**
+ * @brief Adds a new shadow update
+ * @details This adds a new update to the update queue. When the queue is already
+ *   full, this method returns false, otherwise it returns true. The next time
+ *   the manager is updated, the shadow source will recieve an update of its
+ *   shadow map.
+ *
+ * @param source The shadow source to update
+ *
+ * @return Whether the shadow source udpate was sucessfully queued.
+ */
+inline bool ShadowManager::add_update(const ShadowSource* source) {
+  nassertr(_atlas != nullptr, false); // ShadowManager::init not called yet.
+  nassertr(source != nullptr, false); // nullptr-Pointer passed
+
+  if (_queued_updates.size() >= _max_updates) {
+    if (shadowmanager_cat.is_debug()) {
+      shadowmanager_cat.debug() << "cannot update source, out of update slots" << endl;
+    }
+    return false;
+  }
+
+  // Add the update to the queue
+  _queued_updates.push_back(source);
+  return true;
+}
+
+/**
+ * @brief Returns how many update slots are left.
+ * @details This returns how many update slots are left. You can assume the
+ *   next n calls to add_update will succeed, whereas n is the value returned
+ *   by this function.
+ * @return Number of update slots left.
+ */
+inline size_t ShadowManager::get_num_update_slots_left() const {
+  return _max_updates - _queued_updates.size();
+}

+ 157 - 0
contrib/src/rplight/shadowManager.cxx

@@ -0,0 +1,157 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#include "shadowManager.h"
+
+NotifyCategoryDef(shadowmanager, "");
+
+/**
+ * @brief Constructs a new shadow atlas
+ * @details This constructs a new shadow atlas. There are a set of properties
+ *   which should be set before calling ShadowManager::init, see the set-Methods.
+ *   After all properties are set, ShadowManager::init should get called.
+ *   ShadowManager::update should get called on a per frame basis.
+ */
+ShadowManager::ShadowManager() {
+  _max_updates = 10;
+  _atlas = nullptr;
+  _atlas_size = 4096;
+  _tag_state_mgr = nullptr;
+  _atlas_graphics_output = nullptr;
+}
+
+/**
+ * @brief Destructs the ShadowManager
+ * @details This destructs the shadow manager, clearing all resources used
+ */
+ShadowManager::~ShadowManager() {
+  delete _atlas;
+
+  // Todo: Could eventually unregister all shadow cameras. Since the tag state
+  // manager does this on cleanup already, and we get destructed at the same
+  // time (if at all), this is not really necessary
+}
+
+
+/**
+ * @brief Initializes the ShadowManager.
+ * @details This initializes the ShadowManager. All properties should have
+ *   been set before calling this, otherwise assertions will get triggered.
+ *
+ *   This setups everything required for rendering shadows, including the
+ *   shadow atlas and the various shadow cameras. After calling this method,
+ *   no properties can be changed anymore.
+ */
+void ShadowManager::init() {
+  nassertv(!_scene_parent.is_empty());    // Scene parent not set, call set_scene_parent before init!
+  nassertv(_tag_state_mgr != nullptr);     // TagStateManager not set, call set_tag_state_mgr before init!
+  nassertv(_atlas_graphics_output != nullptr); // AtlasGraphicsOutput not set, call set_atlas_graphics_output before init!
+
+  _cameras.resize(_max_updates);
+  _display_regions.resize(_max_updates);
+  _camera_nps.reserve(_max_updates);
+
+  // Create the cameras and regions
+  for(size_t i = 0; i < _max_updates; ++i) {
+
+    // Create the camera
+    PT(Camera) camera = new Camera("ShadowCam-" + format_string(i));
+    camera->set_lens(new MatrixLens());
+    camera->set_active(false);
+    camera->set_scene(_scene_parent);
+    _tag_state_mgr->register_camera("shadow", camera);
+    _camera_nps.push_back(_scene_parent.attach_new_node(camera));
+    _cameras[i] = camera;
+
+    // Create the display region
+    PT(DisplayRegion) region = _atlas_graphics_output->make_display_region();
+    region->set_sort(1000);
+    region->set_clear_depth_active(true);
+    region->set_clear_depth(1.0);
+    region->set_clear_color_active(false);
+    region->set_camera(_camera_nps[i]);
+    region->set_active(false);
+    _display_regions[i] = region;
+  }
+
+  // Create the atlas
+  _atlas = new ShadowAtlas(_atlas_size);
+
+  // Reserve enough space for the updates
+  _queued_updates.reserve(_max_updates);
+}
+
+
+/**
+ * @brief Updates the ShadowManager
+ * @details This updates the ShadowManager, processing all shadow sources which
+ *   need to get updated.
+ *
+ *   This first collects all sources which require an update, sorts them by priority,
+ *   and then processes the first <max_updates> ShadowSources.
+ *
+ *   This may not get called before ShadowManager::init, or an assertion will be
+ *   thrown.
+ */
+void ShadowManager::update() {
+  nassertv(_atlas != nullptr);             // ShadowManager::init not called yet
+  nassertv(_queued_updates.size() <= _max_updates); // Internal error, should not happen
+
+  // Disable all cameras and regions which will not be used
+  for (size_t i = _queued_updates.size(); i < _max_updates; ++i) {
+    _cameras[i]->set_active(false);
+    _display_regions[i]->set_active(false);
+  }
+
+  // Iterate over all queued updates
+  for (size_t i = 0; i < _queued_updates.size(); ++i) {
+    const ShadowSource* source = _queued_updates[i];
+
+    // Enable the camera and display region, so they perform a render
+    _cameras[i]->set_active(true);
+    _display_regions[i]->set_active(true);
+
+    // Set the view projection matrix
+    DCAST(MatrixLens, _cameras[i]->get_lens())->set_user_mat(source->get_mvp());
+
+    // Optional: Show the camera frustum for debugging
+    // _cameras[i]->show_frustum();
+
+    // Set the correct dimensions on the display region
+    const LVecBase4& uv = source->get_uv_region();
+    _display_regions[i]->set_dimensions(
+      uv.get_x(),        // left
+      uv.get_x() + uv.get_z(), // right
+      uv.get_y(),        // bottom
+      uv.get_y() + uv.get_w()  // top
+    );
+  }
+
+  // Clear the update list
+  _queued_updates.clear();
+  _queued_updates.reserve(_max_updates);
+}

+ 91 - 0
contrib/src/rplight/shadowManager.h

@@ -0,0 +1,91 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef SHADOWMANAGER_H
+#define SHADOWMANAGER_H
+
+#include "pandabase.h"
+#include "camera.h"
+#include "luse.h"
+#include "matrixLens.h"
+#include "referenceCount.h"
+#include "nodePath.h"
+#include "displayRegion.h"
+#include "graphicsOutput.h"
+
+#include "tagStateManager.h"
+#include "shadowSource.h"
+#include "shadowAtlas.h"
+
+NotifyCategoryDecl(shadowmanager, EXPORT_CLASS, EXPORT_TEMPL);
+
+
+class ShadowManager : public ReferenceCount {
+PUBLISHED:
+  ShadowManager();
+  ~ShadowManager();
+
+  inline void set_max_updates(size_t max_updates);
+  inline void set_scene(NodePath scene_parent);
+  inline void set_tag_state_manager(TagStateManager* tag_mgr);
+  inline void set_atlas_graphics_output(GraphicsOutput* graphics_output);
+
+  inline void set_atlas_size(size_t atlas_size);
+  inline size_t get_atlas_size() const;
+  MAKE_PROPERTY(atlas_size, get_atlas_size, set_atlas_size);
+
+  inline size_t get_num_update_slots_left() const;
+  MAKE_PROPERTY(num_update_slots_left, get_num_update_slots_left);
+
+  inline ShadowAtlas* get_atlas() const;
+  MAKE_PROPERTY(atlas, get_atlas);
+
+  void init();
+  void update();
+
+public:
+  inline bool add_update(const ShadowSource* source);
+
+private:
+  size_t _max_updates;
+  size_t _atlas_size;
+  NodePath _scene_parent;
+
+  pvector<PT(Camera)> _cameras;
+  pvector<NodePath> _camera_nps;
+  pvector<PT(DisplayRegion)> _display_regions;
+
+  ShadowAtlas* _atlas;
+  TagStateManager* _tag_state_mgr;
+  GraphicsOutput* _atlas_graphics_output;
+
+  typedef pvector<const ShadowSource*> UpdateQueue;
+  UpdateQueue _queued_updates;
+};
+
+#include "shadowManager.I"
+
+#endif // SHADOWMANAGER_H

+ 262 - 0
contrib/src/rplight/shadowSource.I

@@ -0,0 +1,262 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+
+/**
+ * @brief Returns whether the shadow source needs an update.
+ * @details This returns the update flag, which was previously set with
+ *   ShadowSource::set_needs_update. If the value is true, it means that the
+ *   ShadowSource is invalid and should be regenerated. This can either be the
+ *   case because the scene changed and affected the shadow source, or the light
+ *   moved.
+ * @return Update-Flag
+ */
+inline bool ShadowSource::get_needs_update() const {
+  return !has_region() || _needs_update;
+}
+
+/**
+ * @brief Returns the slot of the shadow source.
+ * @details This returns the assigned slot of the ShadowSource, or -1 if no slot
+ *   was assigned yet. You can check if a slot exists with ShadowSource::has_slot.
+ *   The slot is the index of the ShadowSource in the global source buffer.
+ * @return Slot, or -1 to indicate no slot.
+ */
+inline int ShadowSource::get_slot() const {
+  return _slot;
+}
+
+/**
+ * @brief Returns whether the source has a slot.
+ * @details This returns whether the ShadowSource currently has an assigned slot.
+ *   If the source has a slot assigned, this returns true, otherwise false. Cases
+ *   where the source has no slot might be when the source just got attached, but
+ *   never got rendered yet.
+ * @return [description]
+ */
+inline bool ShadowSource::has_slot() const {
+  return _slot >= 0;
+}
+
+/**
+ * @brief Assigns the source a slot
+ * @details This assigns a slot to the ShadowSource. This is called from the
+ *   ShadowManager, when the source gets attached first time. This should not
+ *   get called by the user.
+ *
+ * @param slot Slot of the source, or -1 to indicate no slot.
+ */
+inline void ShadowSource::set_slot(int slot) {
+  _slot = slot;
+}
+
+/**
+ * @brief Setups a perspective lens for the source.
+ * @details This makes the shadow source behave like a perspective lens. The
+ *   parameters are similar to the ones of a PerspectiveLens.
+ *
+ * @param fov FoV of the lens
+ * @param near_plane The near plane of the lens, to avoid artifacts at low distance
+ * @param far_plane The far plane of the lens
+ * @param pos Position of the lens, in world space
+ * @param direction Direction (Orientation) of the lens
+ */
+inline void ShadowSource::
+set_perspective_lens(PN_stdfloat fov, PN_stdfloat near_plane,
+                     PN_stdfloat far_plane, LVecBase3 pos,
+                     LVecBase3 direction) {
+  // Construct the transfo*rmation matrix
+  LMatrix4 transform_mat = LMatrix4::translate_mat(-pos);
+
+  // Construct a temporary lens to generate the lens matrix
+  PerspectiveLens temp_lens = PerspectiveLens(fov, fov);
+  temp_lens.set_film_offset(0, 0);
+  temp_lens.set_near_far(near_plane, far_plane);
+  temp_lens.set_view_vector(direction, LVector3::up());
+  set_matrix_lens(transform_mat * temp_lens.get_projection_mat());
+
+  // Set new bounds, approximate with sphere
+  CPT(BoundingHexahedron) hexahedron = DCAST(BoundingHexahedron, temp_lens.make_bounds());
+  LPoint3 center = (hexahedron->get_min() + hexahedron->get_max()) * 0.5f;
+  _bounds = BoundingSphere(pos + center, (hexahedron->get_max() - center).length());
+}
+
+/**
+ * @brief Sets a custom matrix for the source.
+ * @details This tells the source to use a custom matrix for rendering, just like
+ *   the matrix lens. The matrix should include all transformations, rotations and
+ *   scales. No other matrices will be used for rendering this shadow source (not
+ *   even a coordinate system conversion).
+ *
+ * @param mvp Custom View-Projection matrix
+ */
+inline void ShadowSource::set_matrix_lens(const LMatrix4& mvp) {
+  _mvp = mvp;
+  set_needs_update(true);
+}
+
+/**
+ * @brief Sets the update flag of the source.
+ * @details Sets whether the source is still valid, or needs to get regenerated.
+ *   Usually you only want to flag the shadow source as invalid, by passing
+ *   true as the flag. However, the ShadowManager will set the flag to false
+ *   after updating the source.
+ *
+ * @param flag The update flag
+ */
+inline void ShadowSource::set_needs_update(bool flag) {
+  _needs_update = flag;
+}
+
+/**
+ * @brief Returns whether the source has a valid region.
+ * @details This returns whether the ShadowSource has a valid shadow atlas region
+ *   assigned. This might be not the case when the source never was rendered yet,
+ *   or is about to get updated.
+ * @return true if the source has a valid region, else false.
+ */
+inline bool ShadowSource::has_region() const {
+  return _region.get_x() >= 0 && _region.get_y() >= 0 && _region.get_z() >= 0 && _region.get_w() >= 0;
+}
+
+/**
+ * @brief Returns the resolution of the source.
+ * @details Returns the shadow map resolution of source, in pixels. This is the
+ *   space the source takes in the shadow atlas, in pixels.
+ * @return Resolution in pixels
+ */
+inline size_t ShadowSource::get_resolution() const {
+  return _resolution;
+}
+
+/**
+ * @brief Returns the assigned region of the source in atlas space.
+ * @details This returns the region of the source, in atlas space. This is the
+ *  region set by ShadowSource::set_region. If no region was set yet, returns
+ *  a 4-component integer vector with all components set to -1. To check this,
+ *  you should call ShadowSource::has_region() first.
+ *
+ * @return [description]
+ */
+inline const LVecBase4i& ShadowSource::get_region() const {
+  return _region;
+}
+
+/**
+ * @brief Returns the assigned region of the source in UV space.
+ * @details This returns the region of the source, in UV space. This is the
+ *  region set by ShadowSource::set_region. If no region was set yet, returns
+ *  a 4-component integer vector with all components set to -1. To check this,
+ *  you should call ShadowSource::has_region() first.
+ *
+ * @return [description]
+ */
+inline const LVecBase4& ShadowSource::get_uv_region() const {
+  return _region_uv;
+}
+
+/**
+ * @brief Sets the assigned region of the source in atlas and uv space.
+ * @details This sets the assigned region of the ShadowSource. The region in
+ *   atlas space should be the region returned from the
+ *   ShadowAtlas::find_and_reserve_region. The uv-region should be the same region,
+ *   but in the 0 .. 1 range (can be converted with ShadowAtlas::region_to_uv).
+ *   This is required for the shaders, because they expect coordinates in the
+ *   0 .. 1 range for sampling.
+ *
+ * @param region Atlas-Space region
+ * @param region_uv UV-Space region
+ */
+inline void ShadowSource::set_region(const LVecBase4i& region, const LVecBase4& region_uv) {
+  _region = region;
+  _region_uv = region_uv;
+}
+
+/**
+ * @brief Returns the View-Projection matrix of the source.
+ * @details This returns the current view-projection matrix of the ShadowSource.
+ *   If no matrix was set yet, returns a matrix with all components zero.
+ *   If a matrix was set with ShadowSource::set_matrix_lens, returns the matrix
+ *   set by that function call.
+ *
+ *   If a matrix was set with ShadowSource::set_perspective_lens, returns a
+ *   perspective view-projection matrix setup by those parameters.
+ *
+ *   The matrix returned is the matrix used for rendering the shadow map, and
+ *   includes the camera transform as well as the projection matrix.
+ *
+ * @return View-Projection matrix.
+ */
+inline const LMatrix4& ShadowSource::get_mvp() const {
+  return _mvp;
+}
+
+/**
+ * @brief Writes the source to a GPUCommand.
+ * @details This writes the ShadowSource to a GPUCommand. This stores the
+ *   mvp and the uv-region in the command.
+ *
+ * @param cmd GPUCommand to write to.
+ */
+inline void ShadowSource::write_to_command(GPUCommand &cmd) const {
+  // When storing on the gpu, we should already have a valid slot
+  nassertv(_slot >= 0);
+  cmd.push_mat4(_mvp);
+  cmd.push_vec4(_region_uv);
+}
+
+/**
+ * @brief Sets the resolution of the source.
+ * @details This sets the resolution of the ShadowSource, in pixels. It should be
+ *   a multiple of the tile size of the ShadowAtlas, and greater than zero.
+ *
+ * @param resolution [description]
+ */
+inline void ShadowSource::set_resolution(size_t resolution) {
+  nassertv(resolution > 0);
+  _resolution = resolution;
+  set_needs_update(true);
+}
+
+/**
+ * @brief Returns the shadow sources bounds
+ * @details This returns the bounds of the shadow source, approximated as a sphere
+ * @return Bounds as a BoundingSphere
+ */
+inline const BoundingSphere& ShadowSource::get_bounds() const {
+  return _bounds;
+}
+
+/**
+ * @brief Clears the assigned region of the source
+ * @details This unassigns any shadow atlas region from the source, previously
+ *   set with set_region
+ */
+inline void ShadowSource::clear_region() {
+  _region.fill(-1);
+  _region_uv.fill(0);
+}

+ 41 - 0
contrib/src/rplight/shadowSource.cxx

@@ -0,0 +1,41 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#include "shadowSource.h"
+
+/**
+ * @brief Constructs a new shadow source
+ * @details This constructs a new shadow source, with no projection setup,
+ *   and no slot assigned.
+ */
+ShadowSource::ShadowSource() {
+  _slot = -1;
+  _needs_update = true;
+  _resolution = 512;
+  _mvp.fill(0.0);
+  _region.fill(-1);
+  _region_uv.fill(0);
+}

+ 93 - 0
contrib/src/rplight/shadowSource.h

@@ -0,0 +1,93 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#ifndef SHADOWSOURCE_H
+#define SHADOWSOURCE_H
+
+#include "pandabase.h"
+#include "luse.h"
+#include "transformState.h"
+#include "look_at.h"
+#include "compose_matrix.h"
+#include "perspectiveLens.h"
+#include "boundingVolume.h"
+#include "boundingSphere.h"
+#include "boundingHexahedron.h"
+#include "geometricBoundingVolume.h"
+
+#include "gpuCommand.h"
+
+/**
+ * @brief This class represents a single shadow source.
+ * @details The ShadowSource can be seen as a Camera. It is used by the Lights
+ *   to render their shadows. Each ShadowSource has a position in the atlas,
+ *   and a view-projection matrix. The shadow manager regenerates the shadow maps
+ *   using the data from the shadow sources.
+ */
+class ShadowSource {
+public:
+  ShadowSource();
+
+  inline void write_to_command(GPUCommand &cmd) const;
+
+  inline void set_needs_update(bool flag);
+  inline void set_slot(int slot);
+  inline void set_region(const LVecBase4i& region, const LVecBase4& region_uv);
+  inline void set_resolution(size_t resolution);
+  inline void set_perspective_lens(PN_stdfloat fov, PN_stdfloat near_plane,
+                                   PN_stdfloat far_plane, LVecBase3 pos,
+                                   LVecBase3 direction);
+  inline void set_matrix_lens(const LMatrix4& mvp);
+
+  inline bool has_region() const;
+  inline bool has_slot() const;
+
+  inline void clear_region();
+
+  inline int get_slot() const;
+  inline bool get_needs_update() const;
+  inline size_t get_resolution() const;
+  inline const LMatrix4& get_mvp() const;
+  inline const LVecBase4i& get_region() const;
+  inline const LVecBase4& get_uv_region() const;
+
+  inline const BoundingSphere& get_bounds() const;
+
+private:
+  int _slot;
+  bool _needs_update;
+  size_t _resolution;
+  LMatrix4 _mvp;
+  LVecBase4i _region;
+  LVecBase4 _region_uv;
+
+  BoundingSphere _bounds;
+};
+
+#include "shadowSource.I"
+
+#endif // SHADOWSOURCE_H

+ 93 - 0
contrib/src/rplight/tagStateManager.I

@@ -0,0 +1,93 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+/**
+ * @brief Registers a new camera which renders a certain pass
+ * @details This registers a new camera which will be used to render the given
+ *   pass. The TagStateManager will keep track of the camera and
+ *   applies all registered states onto the camera with Camera::set_tag_state.
+ *   It also applies the appropriate camera mask to the camera,
+ *   and sets an initial state to disable color write depending on the pass.
+ *
+ * @param source Camera which will be used to render shadows
+ */
+inline void TagStateManager::
+register_camera(const string& name, Camera* source) {
+  ContainerList::iterator entry = _containers.find(name);
+  nassertv(entry != _containers.end());
+  register_camera(entry->second, source);
+}
+
+/**
+ * @brief Unregisters a camera from the list of shadow cameras
+ * @details This unregisters a camera from the list of shadows cameras. It also
+ *   resets all tag states of the camera, and also its initial state.
+ *
+ * @param source Camera to unregister
+ */
+inline void TagStateManager::
+unregister_camera(const string& name, Camera* source) {
+  ContainerList::iterator entry = _containers.find(name);
+  nassertv(entry != _containers.end());
+  unregister_camera(entry->second, source);
+}
+
+/**
+ * @brief Applies a given state for a pass to a NodePath
+ * @details This applies a shader to the given NodePath which is used when the
+ *   NodePath is rendered by any registered camera for that pass.
+ *   It also disables color write depending on the pass.
+ *
+ * @param np The nodepath to apply the shader to
+ * @param shader A handle to the shader to apply
+ * @param name Name of the state, should be a unique identifier
+ * @param sort Determines the sort with which the shader will be applied.
+ */
+inline void TagStateManager::
+apply_state(const string& state, NodePath np, Shader* shader,
+            const string &name, int sort) {
+  ContainerList::iterator entry = _containers.find(state);
+  nassertv(entry != _containers.end());
+  apply_state(entry->second, np, shader, name, sort);
+}
+
+/**
+ * @brief Returns the render mask for the given state
+ * @details This returns the mask of a given render pass, which can be used
+ *   to either show or hide objects from this pass.
+ *
+ * @param container_name Name of the render-pass
+ * @return Bit mask of the render pass
+ */
+inline BitMask32 TagStateManager::
+get_mask(const string &container_name) {
+  if (container_name == "gbuffer") {
+    return BitMask32::bit(1);
+  }
+  ContainerList::iterator entry = _containers.find(container_name);
+  nassertr(entry != _containers.end(), BitMask32());
+  return entry->second.mask;
+}

+ 202 - 0
contrib/src/rplight/tagStateManager.cxx

@@ -0,0 +1,202 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+
+#include "tagStateManager.h"
+
+
+NotifyCategoryDef(tagstatemgr, "");
+
+/**
+ * @brief Constructs a new TagStateManager
+ * @details This constructs a new TagStateManager. The #main_cam_node should
+ *   refer to the main scene camera, and will most likely be base.cam.
+ *   It is necessary to pass the camera because the C++ code does not have
+ *   access to the showbase.
+ *
+ * @param main_cam_node The main scene camera
+ */
+TagStateManager::
+TagStateManager(NodePath main_cam_node) {
+  nassertv(!main_cam_node.is_empty());
+  nassertv(DCAST(Camera, main_cam_node.node()) != nullptr);
+  _main_cam_node = main_cam_node;
+
+  // Set default camera mask
+  DCAST(Camera, _main_cam_node.node())->set_camera_mask(BitMask32::bit(1));
+
+  // Init containers
+  _containers["shadow"]   = StateContainer("Shadows",  2, false);
+  _containers["voxelize"] = StateContainer("Voxelize", 3, false);
+  _containers["envmap"]   = StateContainer("Envmap",   4, true);
+  _containers["forward"]  = StateContainer("Forward",  5, true);
+}
+
+/**
+ * @brief Destructs the TagStateManager
+ * @details This destructs the TagStateManager, and cleans up all resources used.
+ */
+TagStateManager::
+~TagStateManager() {
+  cleanup_states();
+}
+
+/**
+ * @brief Applies a given state to a NodePath
+ * @details This applies a shader to the given NodePath which is used when the
+ *   NodePath is rendered by any registered camera of the container.
+ *
+ * @param container The container which is used to store the state
+ * @param np The nodepath to apply the shader to
+ * @param shader A handle to the shader to apply
+ * @param name Name of the state, should be a unique identifier
+ * @param sort Changes the sort with which the shader will be applied.
+ */
+void TagStateManager::
+apply_state(StateContainer& container, NodePath np, Shader* shader,
+            const string &name, int sort) {
+  if (tagstatemgr_cat.is_spam()) {
+    tagstatemgr_cat.spam() << "Constructing new state " << name
+                 << " with shader " << shader << endl;
+  }
+
+  // Construct the render state
+  CPT(RenderState) state = RenderState::make_empty();
+
+  // Disable color write for all stages except the environment container
+  if (!container.write_color) {
+    state = state->set_attrib(ColorWriteAttrib::make(ColorWriteAttrib::C_off), 10000);
+  }
+  state = state->set_attrib(ShaderAttrib::make(shader, sort), sort);
+
+  // Emit a warning if we override an existing state
+  if (container.tag_states.count(name) != 0) {
+    tagstatemgr_cat.warning() << "Overriding existing state " << name << endl;
+  }
+
+  // Store the state, this is required whenever we attach a new camera, so
+  // it can also track the existing states
+  container.tag_states[name] = state;
+
+  // Save the tag on the node path
+  np.set_tag(container.tag_name, name);
+
+  // Apply the state on all cameras which are attached so far
+  for (size_t i = 0; i < container.cameras.size(); ++i) {
+    container.cameras[i]->set_tag_state(name, state);
+  }
+}
+
+/**
+ * @brief Cleans up all registered states.
+ * @details This cleans up all states which were registered to the TagStateManager.
+ *   It also calls Camera::clear_tag_states() on the main_cam_node and all attached
+ *   cameras.
+ */
+void TagStateManager::
+cleanup_states() {
+  if (tagstatemgr_cat.is_info()) {
+    tagstatemgr_cat.info() << "cleaning up states" << endl;
+  }
+
+  // Clear all tag states of the main camera
+  DCAST(Camera, _main_cam_node.node())->clear_tag_states();
+
+  // Clear the containers
+  // XXX: Just iterate over the _container map
+  cleanup_container_states(_containers["shadow"]);
+  cleanup_container_states(_containers["voxelize"]);
+  cleanup_container_states(_containers["envmap"]);
+  cleanup_container_states(_containers["forward"]);
+}
+
+/**
+ * @brief Cleans up the states of a given container
+ * @details This cleans all tag states of the given container,
+ *   and also calls Camera::clear_tag_states on every assigned camera.
+ *
+ * @param container Container to clear
+ */
+void TagStateManager::
+cleanup_container_states(StateContainer& container) {
+  for (size_t i = 0; i < container.cameras.size(); ++i) {
+    container.cameras[i]->clear_tag_states();
+  }
+  container.tag_states.clear();
+}
+
+/**
+ * @brief Registers a new camera to a given container
+ * @details This registers a new camera to a container, and sets its initial
+ *   state as well as the camera mask.
+ *
+ * @param container The container to add the camera to
+ * @param source The camera to add
+ */
+void TagStateManager::
+register_camera(StateContainer& container, Camera* source) {
+  source->set_tag_state_key(container.tag_name);
+  source->set_camera_mask(container.mask);
+
+  // Construct an initial state which also disables color write, additionally
+  // to the ColorWriteAttrib on each unique state.
+  CPT(RenderState) state = RenderState::make_empty();
+
+  if (!container.write_color) {
+    state = state->set_attrib(ColorWriteAttrib::make(ColorWriteAttrib::C_off), 10000);
+  }
+  source->set_initial_state(state);
+
+  // Store the camera so we can keep track of it
+  container.cameras.push_back(source);
+}
+
+/**
+ * @brief Unregisters a camera from a container
+ * @details This unregisters a camera from the list of cameras of a given
+ *   container. It also resets all tag states of the camera, and also its initial
+ *   state.
+ *
+ * @param source Camera to unregister
+ */
+void TagStateManager::
+unregister_camera(StateContainer& container, Camera* source) {
+  CameraList& cameras = container.cameras;
+
+  // Make sure the camera was attached so far
+  if (std::find(cameras.begin(), cameras.end(), source) == cameras.end()) {
+    tagstatemgr_cat.error()
+      << "Called unregister_camera but camera was never registered!" << endl;
+    return;
+  }
+
+  // Remove the camera from the list of attached cameras
+  cameras.erase(std::remove(cameras.begin(), cameras.end(), source), cameras.end());
+
+  // Reset the camera
+  source->clear_tag_states();
+  source->set_initial_state(RenderState::make_empty());
+}

+ 93 - 0
contrib/src/rplight/tagStateManager.h

@@ -0,0 +1,93 @@
+/**
+ *
+ * RenderPipeline
+ *
+ * Copyright (c) 2014-2016 tobspr <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef TAGSTATEMANAGER_H
+#define TAGSTATEMANAGER_H
+
+#include "pandabase.h"
+#include "bitMask.h"
+#include "camera.h"
+#include "nodePath.h"
+#include "shader.h"
+#include "renderState.h"
+#include "shaderAttrib.h"
+#include "colorWriteAttrib.h"
+
+NotifyCategoryDecl(tagstatemgr, EXPORT_CLASS, EXPORT_TEMPL);
+
+/**
+ * @brief This class handles all different tag states
+ * @details The TagStateManager stores a list of RenderStates assigned to different
+ *   steps in the pipeline. For example, there are a list of shadow states, which
+ *   are applied whenever objects are rendered from a shadow camera.
+ *
+ *   The Manager also stores a list of all cameras used in the different stages,
+ *   to keep track of the states used and to be able to attach new states.
+ */
+class TagStateManager {
+PUBLISHED:
+  TagStateManager(NodePath main_cam_node);
+  ~TagStateManager();
+
+  inline void apply_state(const string& state, NodePath np, Shader* shader, const string &name, int sort);
+  void cleanup_states();
+
+  inline void register_camera(const string& state, Camera* source);
+  inline void unregister_camera(const string& state, Camera* source);
+  inline BitMask32 get_mask(const string &container_name);
+
+private:
+  typedef vector<Camera*> CameraList;
+  typedef pmap<string, CPT(RenderState)> TagStateList;
+
+  struct StateContainer {
+    CameraList cameras;
+    TagStateList tag_states;
+    string tag_name;
+    BitMask32 mask;
+    bool write_color;
+
+    StateContainer() {};
+    StateContainer(const string &tag_name, size_t mask, bool write_color)
+      : tag_name(tag_name), mask(BitMask32::bit(mask)), write_color(write_color) {};
+  };
+
+  void apply_state(StateContainer& container, NodePath np, Shader* shader,
+                   const string& name, int sort);
+  void cleanup_container_states(StateContainer& container);
+  void register_camera(StateContainer &container, Camera* source);
+  void unregister_camera(StateContainer &container, Camera* source);
+
+  typedef pmap<string, StateContainer> ContainerList;
+  ContainerList _containers;
+
+  NodePath _main_cam_node;
+};
+
+
+#include "tagStateManager.I"
+
+#endif // TAGSTATEMANAGER_H

+ 4 - 4
contrib/src/sceneeditor/seFileSaver.py

@@ -488,7 +488,7 @@ class FileSaver:
                      out_file.write (i2+ "alight = AmbientLight(\'"+ light.getName() +"\')\n")
                      out_file.write (i2+ "alight = AmbientLight(\'"+ light.getName() +"\')\n")
                      out_file.write (i2+ "alight.setColor(VBase4("+ str(light.getLightColor().getX())+ "," + str(light.getLightColor().getY())+ "," + str(light.getLightColor().getZ()) + "," + str(light.getLightColor().getW()) + "))\n")
                      out_file.write (i2+ "alight.setColor(VBase4("+ str(light.getLightColor().getX())+ "," + str(light.getLightColor().getY())+ "," + str(light.getLightColor().getZ()) + "," + str(light.getLightColor().getW()) + "))\n")
                      out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n")
                      out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n")
-                     out_file.write (i2+ "self."+light.getName()+"= render.attachNewNode(alight.upcastToPandaNode())\n")
+                     out_file.write (i2+ "self."+light.getName()+"= render.attachNewNode(alight)\n")
                      out_file.write (i2+ "self."+light.getName()+".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n")
                      out_file.write (i2+ "self."+light.getName()+".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n")
                      out_file.write (i2+ "self.LightDict[\'" + light.getName() + "\']=alight\n")
                      out_file.write (i2+ "self.LightDict[\'" + light.getName() + "\']=alight\n")
                      out_file.write (i2+ "self.LightTypes[\'" + light.getName() + "\']=\'" + type + "\'\n")
                      out_file.write (i2+ "self.LightTypes[\'" + light.getName() + "\']=\'" + type + "\'\n")
@@ -503,7 +503,7 @@ class FileSaver:
                      #out_file.write (i2+ "alight.setPoint(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n")
                      #out_file.write (i2+ "alight.setPoint(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n")
                      out_file.write (i2+ "alight.setSpecularColor(Vec4(" + str(light.getSpecColor().getX()) + "," + str(light.getSpecColor().getY()) + "," + str(light.getSpecColor().getZ()) + "," + str(light.getSpecColor().getW()) + "))\n")
                      out_file.write (i2+ "alight.setSpecularColor(Vec4(" + str(light.getSpecColor().getX()) + "," + str(light.getSpecColor().getY()) + "," + str(light.getSpecColor().getZ()) + "," + str(light.getSpecColor().getW()) + "))\n")
                      out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n")
                      out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n")
-                     out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight.upcastToPandaNode())\n")
+                     out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight)\n")
                      out_file.write (i2+ "self."+light.getName()+ ".setPos(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n")
                      out_file.write (i2+ "self."+light.getName()+ ".setPos(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n")
                      out_file.write (i2+ "self."+light.getName()+ ".setHpr(Vec3("+ str(light.getH())+ "," + str(light.getP())+ "," + str(light.getR()) + "))\n")
                      out_file.write (i2+ "self."+light.getName()+ ".setHpr(Vec3("+ str(light.getH())+ "," + str(light.getP())+ "," + str(light.getR()) + "))\n")
                      out_file.write (i2+ "self."+light.getName()+ ".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n")
                      out_file.write (i2+ "self."+light.getName()+ ".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n")
@@ -521,7 +521,7 @@ class FileSaver:
                      out_file.write (i2+ "alight.setSpecularColor(Vec4(" + str(light.getSpecColor().getX()) + "," + str(light.getSpecColor().getY()) + "," + str(light.getSpecColor().getZ()) + "," + str(light.getSpecColor().getW()) + "))\n")
                      out_file.write (i2+ "alight.setSpecularColor(Vec4(" + str(light.getSpecColor().getX()) + "," + str(light.getSpecColor().getY()) + "," + str(light.getSpecColor().getZ()) + "," + str(light.getSpecColor().getW()) + "))\n")
                      out_file.write (i2+ "alight.setAttenuation(Vec3("+ str(light.getAttenuation().getX()) + "," + str(light.getAttenuation().getY()) + "," + str(light.getAttenuation().getZ()) + "))\n")
                      out_file.write (i2+ "alight.setAttenuation(Vec3("+ str(light.getAttenuation().getX()) + "," + str(light.getAttenuation().getY()) + "," + str(light.getAttenuation().getZ()) + "))\n")
                      out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n")
                      out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n")
-                     out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight.upcastToPandaNode())\n")
+                     out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight)\n")
                      out_file.write (i2+ "self."+light.getName()+ ".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n")
                      out_file.write (i2+ "self."+light.getName()+ ".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n")
                      out_file.write (i2+ "self."+light.getName()+ ".setPos(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n")
                      out_file.write (i2+ "self."+light.getName()+ ".setPos(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n")
                      out_file.write (i2+ "self.LightDict[\'" + light.getName() + "\']=alight\n")
                      out_file.write (i2+ "self.LightDict[\'" + light.getName() + "\']=alight\n")
@@ -539,7 +539,7 @@ class FileSaver:
                      out_file.write (i2+ "alight.setAttenuation(Vec3("+ str(light.getAttenuation().getX()) + "," + str(light.getAttenuation().getY()) + "," + str(light.getAttenuation().getZ()) + "))\n")
                      out_file.write (i2+ "alight.setAttenuation(Vec3("+ str(light.getAttenuation().getX()) + "," + str(light.getAttenuation().getY()) + "," + str(light.getAttenuation().getZ()) + "))\n")
                      out_file.write (i2+ "alight.setExponent(" +str(light.getExponent()) +")\n")
                      out_file.write (i2+ "alight.setExponent(" +str(light.getExponent()) +")\n")
                      out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n")
                      out_file.write (i2+ "self.lightAttrib=self.lightAttrib.addLight(alight)\n")
-                     out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight.upcastToLensNode())\n")
+                     out_file.write (i2+ "self."+light.getName()+ "= render.attachNewNode(alight)\n")
                      out_file.write (i2+ "self."+light.getName()+ ".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n")
                      out_file.write (i2+ "self."+light.getName()+ ".setTag(\"Metadata\",\"" + light.getTag("Metadata") + "\")\n")
                      out_file.write (i2+ "self."+light.getName()+ ".setPos(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n")
                      out_file.write (i2+ "self."+light.getName()+ ".setPos(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\n")
                      out_file.write (i2+ "self."+light.getName()+ ".setHpr(Vec3("+ str(light.getH())+ "," + str(light.getP())+ "," + str(light.getR()) + "))\n")
                      out_file.write (i2+ "self."+light.getName()+ ".setHpr(Vec3("+ str(light.getH())+ "," + str(light.getP())+ "," + str(light.getR()) + "))\n")

+ 1 - 8
contrib/src/sceneeditor/seLights.py

@@ -63,13 +63,8 @@ class seLight(NodePath):
         self.lence = lence
         self.lence = lence
         self.active = True
         self.active = True
 
 
-        if isinstance(light, Spotlight):
-            node = light.upcastToLensNode()
-        else:
-            node = light.upcastToPandaNode()
-
         # Attach node to self
         # Attach node to self
-        self.LightNode=parent.attachNewNode(node)
+        self.LightNode=parent.attachNewNode(light)
         self.LightNode.setTag("Metadata",tag)
         self.LightNode.setTag("Metadata",tag)
         if(self.type=='spot'):
         if(self.type=='spot'):
             self.LightNode.setHpr(self.orientation)
             self.LightNode.setHpr(self.orientation)
@@ -418,8 +413,6 @@ class seLightManager(NodePath):
         #################################################################
         #################################################################
         type = lower(light.getType().getName())
         type = lower(light.getType().getName())
 
 
-        light.upcastToNamable()
-
         specularColor = VBase4(1)
         specularColor = VBase4(1)
         position = Point3(0,0,0)
         position = Point3(0,0,0)
         orientation = Vec3(1,0,0)
         orientation = Vec3(1,0,0)

+ 2 - 2
direct/src/actor/Actor.py

@@ -1103,8 +1103,8 @@ class Actor(DirectObject, NodePath):
         # Get a handle to the joint.
         # Get a handle to the joint.
         joint = bundle.findChild(jointName)
         joint = bundle.findChild(jointName)
 
 
-        if node == None:
-            node = self.attachNewNode(jointName)
+        if node is None:
+            node = partDef.partBundleNP.attachNewNode(jointName)
 
 
         if (joint):
         if (joint):
             if localTransform:
             if localTransform:

+ 6 - 85
direct/src/dcparser/dcClass.cxx

@@ -514,15 +514,9 @@ receive_update_broadcast_required_owner(PyObject *distobj,
   for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
   for (int i = 0; i < num_fields && !PyErr_Occurred(); ++i) {
     DCField *field = get_inherited_field(i);
     DCField *field = get_inherited_field(i);
     if (field->as_molecular_field() == (DCMolecularField *)NULL &&
     if (field->as_molecular_field() == (DCMolecularField *)NULL &&
-        field->is_required()) {
+        field->is_required() && (field->is_ownrecv() || field->is_broadcast())) {
       packer.begin_unpack(field);
       packer.begin_unpack(field);
-      if (field->is_ownrecv()) {
-        field->receive_update(packer, distobj);
-      } else {
-        // It's not an ownrecv field; skip over it.  It's difficult to filter
-        // this on the server, ask Roger for the reason.
-        packer.unpack_skip();
-      }
+      field->receive_update(packer, distobj);
       if (!packer.end_unpack()) {
       if (!packer.end_unpack()) {
         break;
         break;
       }
       }
@@ -946,19 +940,17 @@ ai_format_generate(PyObject *distobj, DOID_TYPE do_id,
   bool has_optional_fields = (PyObject_IsTrue(optional_fields) != 0);
   bool has_optional_fields = (PyObject_IsTrue(optional_fields) != 0);
 
 
   if (has_optional_fields) {
   if (has_optional_fields) {
-    packer.raw_pack_uint16(STATESERVER_OBJECT_GENERATE_WITH_REQUIRED_OTHER);
+    packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED_OTHER);
   } else {
   } else {
-    packer.raw_pack_uint16(STATESERVER_OBJECT_GENERATE_WITH_REQUIRED);
+    packer.raw_pack_uint16(STATESERVER_CREATE_OBJECT_WITH_REQUIRED);
   }
   }
 
 
+  packer.raw_pack_uint32(do_id);
   // Parent is a bit overloaded; this parent is not about inheritance, this
   // Parent is a bit overloaded; this parent is not about inheritance, this
   // one is about the visibility container parent, i.e.  the zone parent:
   // one is about the visibility container parent, i.e.  the zone parent:
-  if (parent_id) {
-    packer.raw_pack_uint32(parent_id);
-  }
+  packer.raw_pack_uint32(parent_id);
   packer.raw_pack_uint32(zone_id);
   packer.raw_pack_uint32(zone_id);
   packer.raw_pack_uint16(_number);
   packer.raw_pack_uint16(_number);
-  packer.raw_pack_uint32(do_id);
 
 
   // Specify all of the required fields.
   // Specify all of the required fields.
   int num_fields = get_num_inherited_fields();
   int num_fields = get_num_inherited_fields();
@@ -1009,77 +1001,6 @@ ai_format_generate(PyObject *distobj, DOID_TYPE do_id,
   return Datagram(packer.get_data(), packer.get_length());
   return Datagram(packer.get_data(), packer.get_length());
 }
 }
 #endif  // HAVE_PYTHON
 #endif  // HAVE_PYTHON
-#ifdef HAVE_PYTHON
-/**
- * Generates a datagram containing the message necessary to create a new
- * database distributed object from the AI.
- *
- * First Pass is to only include required values (with Defaults).
- */
-Datagram DCClass::
-ai_database_generate_context(
-    unsigned int context_id, DOID_TYPE parent_id, ZONEID_TYPE zone_id,
-    CHANNEL_TYPE owner_channel,
-    CHANNEL_TYPE database_server_id, CHANNEL_TYPE from_channel_id) const
-{
-  DCPacker packer;
-  packer.raw_pack_uint8(1);
-  packer.RAW_PACK_CHANNEL(database_server_id);
-  packer.RAW_PACK_CHANNEL(from_channel_id);
-  // packer.raw_pack_uint8('A');
-  packer.raw_pack_uint16(STATESERVER_OBJECT_CREATE_WITH_REQUIRED_CONTEXT);
-  packer.raw_pack_uint32(parent_id);
-  packer.raw_pack_uint32(zone_id);
-  packer.RAW_PACK_CHANNEL(owner_channel);
-  packer.raw_pack_uint16(_number); // DCD class ID
-  packer.raw_pack_uint32(context_id);
-
-  // Specify all of the required fields.
-  int num_fields = get_num_inherited_fields();
-  for (int i = 0; i < num_fields; ++i) {
-    DCField *field = get_inherited_field(i);
-    if (field->is_required() && field->as_molecular_field() == NULL) {
-      packer.begin_pack(field);
-      packer.pack_default_value();
-      packer.end_pack();
-    }
-  }
-
-  return Datagram(packer.get_data(), packer.get_length());
-}
-#endif  // HAVE_PYTHON
-
-#ifdef HAVE_PYTHON
-Datagram DCClass::
-ai_database_generate_context_old(
-    unsigned int context_id, DOID_TYPE parent_id, ZONEID_TYPE zone_id,
-    CHANNEL_TYPE database_server_id, CHANNEL_TYPE from_channel_id) const
-{
-  DCPacker packer;
-  packer.raw_pack_uint8(1);
-  packer.RAW_PACK_CHANNEL(database_server_id);
-  packer.RAW_PACK_CHANNEL(from_channel_id);
-  // packer.raw_pack_uint8('A');
-  packer.raw_pack_uint16(STATESERVER_OBJECT_CREATE_WITH_REQUIRED_CONTEXT);
-  packer.raw_pack_uint32(parent_id);
-  packer.raw_pack_uint32(zone_id);
-  packer.raw_pack_uint16(_number); // DCD class ID
-  packer.raw_pack_uint32(context_id);
-
-  // Specify all of the required fields.
-  int num_fields = get_num_inherited_fields();
-  for (int i = 0; i < num_fields; ++i) {
-    DCField *field = get_inherited_field(i);
-    if (field->is_required() && field->as_molecular_field() == NULL) {
-      packer.begin_pack(field);
-      packer.pack_default_value();
-      packer.end_pack();
-    }
-  }
-
-  return Datagram(packer.get_data(), packer.get_length());
-}
-#endif  // HAVE_PYTHON
 
 
 /**
 /**
  * Write a string representation of this instance to <out>.
  * Write a string representation of this instance to <out>.

+ 0 - 5
direct/src/dcparser/dcClass.h

@@ -117,11 +117,6 @@ PUBLISHED:
   Datagram client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
   Datagram client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
                                       ZONEID_TYPE zone_id,                                                           PyObject *optional_fields) const;
                                       ZONEID_TYPE zone_id,                                                           PyObject *optional_fields) const;
 
 
-  Datagram ai_database_generate_context(unsigned int context_id, DOID_TYPE parent_id, ZONEID_TYPE zone_id, CHANNEL_TYPE owner_channel,
-                                CHANNEL_TYPE database_server_id, CHANNEL_TYPE from_channel_id) const;
-  Datagram ai_database_generate_context_old(unsigned int context_id, DOID_TYPE parent_id, ZONEID_TYPE zone_id,
-                                CHANNEL_TYPE database_server_id, CHANNEL_TYPE from_channel_id) const;
-
 #endif
 #endif
 
 
 public:
 public:

+ 2 - 2
direct/src/dcparser/dcField.cxx

@@ -391,7 +391,7 @@ Datagram DCField::
 client_format_update(DOID_TYPE do_id, PyObject *args) const {
 client_format_update(DOID_TYPE do_id, PyObject *args) const {
   DCPacker packer;
   DCPacker packer;
 
 
-  packer.raw_pack_uint16(CLIENT_OBJECT_UPDATE_FIELD);
+  packer.raw_pack_uint16(CLIENT_OBJECT_SET_FIELD);
   packer.raw_pack_uint32(do_id);
   packer.raw_pack_uint32(do_id);
   packer.raw_pack_uint16(_number);
   packer.raw_pack_uint16(_number);
 
 
@@ -417,7 +417,7 @@ ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyOb
   packer.raw_pack_uint8(1);
   packer.raw_pack_uint8(1);
   packer.RAW_PACK_CHANNEL(to_id);
   packer.RAW_PACK_CHANNEL(to_id);
   packer.RAW_PACK_CHANNEL(from_id);
   packer.RAW_PACK_CHANNEL(from_id);
-  packer.raw_pack_uint16(STATESERVER_OBJECT_UPDATE_FIELD);
+  packer.raw_pack_uint16(STATESERVER_OBJECT_SET_FIELD);
   packer.raw_pack_uint32(do_id);
   packer.raw_pack_uint32(do_id);
   packer.raw_pack_uint16(_number);
   packer.raw_pack_uint16(_number);
 
 

+ 6 - 9
direct/src/dcparser/dcmsgtypes.h

@@ -17,16 +17,13 @@
 // This file defines the server message types used within this module.  It
 // This file defines the server message types used within this module.  It
 // duplicates some symbols defined in MsgTypes.py and AIMsgTypes.py.
 // duplicates some symbols defined in MsgTypes.py and AIMsgTypes.py.
 
 
-#define CLIENT_OBJECT_UPDATE_FIELD                        24
-#define CLIENT_CREATE_OBJECT_REQUIRED                     34
-#define CLIENT_CREATE_OBJECT_REQUIRED_OTHER               35
+#define CLIENT_OBJECT_SET_FIELD                           120
+#define CLIENT_ENTER_OBJECT_REQUIRED                      142
+#define CLIENT_ENTER_OBJECT_REQUIRED_OTHER                143
 
 
-#define STATESERVER_OBJECT_GENERATE_WITH_REQUIRED         2001
-#define STATESERVER_OBJECT_GENERATE_WITH_REQUIRED_OTHER   2003
-#define STATESERVER_OBJECT_UPDATE_FIELD                   2004
-#define STATESERVER_OBJECT_CREATE_WITH_REQUIRED_CONTEXT   2050
-#define STATESERVER_OBJECT_CREATE_WITH_REQUIR_OTHER_CONTEXT  2051
-#define STATESERVER_BOUNCE_MESSAGE                        2086
+#define STATESERVER_CREATE_OBJECT_WITH_REQUIRED           2000
+#define STATESERVER_CREATE_OBJECT_WITH_REQUIRED_OTHER     2001
+#define STATESERVER_OBJECT_SET_FIELD                      2020
 
 
 #define CLIENT_OBJECT_GENERATE_CMU                        9002
 #define CLIENT_OBJECT_GENERATE_CMU                        9002
 
 

+ 2 - 2
direct/src/directtools/DirectSelection.py

@@ -623,8 +623,8 @@ class SelectionRay(SelectionQueue):
     def pickBitMask(self, bitMask = BitMask32.allOff(),
     def pickBitMask(self, bitMask = BitMask32.allOff(),
                     targetNodePath = None,
                     targetNodePath = None,
                     skipFlags = SKIP_ALL):
                     skipFlags = SKIP_ALL):
-        if parentNodePath is None:
-            parentNodePath = render
+        if targetNodePath is None:
+            targetNodePath = render
         self.collideWithBitMask(bitMask)
         self.collideWithBitMask(bitMask)
         self.pick(targetNodePath)
         self.pick(targetNodePath)
         # Determine collision entry
         # Determine collision entry

+ 2 - 0
direct/src/distributed/CRDataCache.py

@@ -2,6 +2,8 @@ from direct.distributed.CachedDOData import CachedDOData
 from panda3d.core import ConfigVariableInt
 from panda3d.core import ConfigVariableInt
 
 
 
 
+__all__ = ["CRDataCache"]
+
 class CRDataCache:
 class CRDataCache:
     # Stores cached data for DistributedObjects between instantiations on the client
     # Stores cached data for DistributedObjects between instantiations on the client
 
 

+ 3 - 3
direct/src/distributed/ClientRepositoryBase.py

@@ -175,7 +175,7 @@ class ClientRepositoryBase(ConnectionRepository):
         "generate" messages when they are replayed().
         "generate" messages when they are replayed().
         """
         """
 
 
-        if msgType == CLIENT_CREATE_OBJECT_REQUIRED_OTHER:
+        if msgType == CLIENT_ENTER_OBJECT_REQUIRED_OTHER:
             # It's a generate message.
             # It's a generate message.
             doId = extra
             doId = extra
             if doId in self.deferredDoIds:
             if doId in self.deferredDoIds:
@@ -263,7 +263,7 @@ class ClientRepositoryBase(ConnectionRepository):
             distObj.setLocation(parentId, zoneId)
             distObj.setLocation(parentId, zoneId)
             distObj.updateRequiredFields(dclass, di)
             distObj.updateRequiredFields(dclass, di)
             # updateRequiredFields calls announceGenerate
             # updateRequiredFields calls announceGenerate
-            print("New DO:%s, dclass:%s"%(doId, dclass.getName()))
+            self.notify.debug("New DO:%s, dclass:%s" % (doId, dclass.getName()))
         return distObj
         return distObj
 
 
     def generateWithRequiredOtherFields(self, dclass, doId, di,
     def generateWithRequiredOtherFields(self, dclass, doId, di,
@@ -381,7 +381,7 @@ class ClientRepositoryBase(ConnectionRepository):
             # The object had been deferred.  Great; we don't even have
             # The object had been deferred.  Great; we don't even have
             # to generate it now.
             # to generate it now.
             del self.deferredDoIds[doId]
             del self.deferredDoIds[doId]
-            i = self.deferredGenerates.index((CLIENT_CREATE_OBJECT_REQUIRED_OTHER, doId))
+            i = self.deferredGenerates.index((CLIENT_ENTER_OBJECT_REQUIRED_OTHER, doId))
             del self.deferredGenerates[i]
             del self.deferredGenerates[i]
             if len(self.deferredGenerates) == 0:
             if len(self.deferredGenerates) == 0:
                 taskMgr.remove('deferredGenerate')
                 taskMgr.remove('deferredGenerate')

+ 1 - 0
direct/src/distributed/ConnectionRepository.py

@@ -10,6 +10,7 @@ from .PyDatagramIterator import PyDatagramIterator
 import types
 import types
 import gc
 import gc
 
 
+__all__ = ["ConnectionRepository", "GCTrigger"]
 
 
 class ConnectionRepository(
 class ConnectionRepository(
         DoInterestManager, DoCollectionManager, CConnectionRepository):
         DoInterestManager, DoCollectionManager, CConnectionRepository):

+ 2 - 0
direct/src/distributed/DistributedNode.py

@@ -15,6 +15,8 @@ class DistributedNode(DistributedObject.DistributedObject, NodePath):
             self.DistributedNode_initialized = 1
             self.DistributedNode_initialized = 1
             self.gotStringParentToken = 0
             self.gotStringParentToken = 0
             DistributedObject.DistributedObject.__init__(self, cr)
             DistributedObject.DistributedObject.__init__(self, cr)
+            if not self.this:
+                NodePath.__init__(self, "DistributedNode")
 
 
             # initialize gridParent
             # initialize gridParent
             self.gridParent = None
             self.gridParent = None

+ 0 - 1
direct/src/distributed/DistributedNodeUD.py

@@ -1,4 +1,3 @@
-#from otp.ai.AIBaseGlobal import *
 from .DistributedObjectUD import DistributedObjectUD
 from .DistributedObjectUD import DistributedObjectUD
 
 
 class DistributedNodeUD(DistributedObjectUD):
 class DistributedNodeUD(DistributedObjectUD):

+ 7 - 15
direct/src/distributed/DistributedObjectAI.py

@@ -146,8 +146,6 @@ class DistributedObjectAI(DistributedObjectBase):
                     barrier.cleanup()
                     barrier.cleanup()
                 self.__barriers = {}
                 self.__barriers = {}
 
 
-                self.air.stopTrackRequestDeletedDO(self)
-
                 # DCR: I've re-enabled this block of code so that Toontown's
                 # DCR: I've re-enabled this block of code so that Toontown's
                 # AI won't leak channels.
                 # AI won't leak channels.
                 # Let me know if it causes trouble.
                 # Let me know if it causes trouble.
@@ -155,10 +153,9 @@ class DistributedObjectAI(DistributedObjectBase):
                 ### block until a solution is thought out of how to prevent
                 ### block until a solution is thought out of how to prevent
                 ### this delete message or to handle this message better
                 ### this delete message or to handle this message better
                 # TODO: do we still need this check?
                 # TODO: do we still need this check?
-                if not hasattr(self, "doNotDeallocateChannel"):
-                    if self.air and not hasattr(self.air, "doNotDeallocateChannel"):
-                        if self.air.minChannel <= self.doId <= self.air.maxChannel:
-                            self.air.deallocateChannel(self.doId)
+                if not getattr(self, "doNotDeallocateChannel", False):
+                    if self.air:
+                        self.air.deallocateChannel(self.doId)
                 self.air = None
                 self.air = None
 
 
                 self.parentId = None
                 self.parentId = None
@@ -200,9 +197,6 @@ class DistributedObjectAI(DistributedObjectBase):
         """
         """
         pass
         pass
 
 
-    def addInterest(self, zoneId, note="", event=None):
-        self.air.addInterest(self.doId, zoneId, note, event)
-
     def b_setLocation(self, parentId, zoneId):
     def b_setLocation(self, parentId, zoneId):
         self.d_setLocation(parentId, zoneId)
         self.d_setLocation(parentId, zoneId)
         self.setLocation(parentId, zoneId)
         self.setLocation(parentId, zoneId)
@@ -274,9 +268,6 @@ class DistributedObjectAI(DistributedObjectBase):
 
 
         dclass.receiveUpdateOther(self, di)
         dclass.receiveUpdateOther(self, di)
 
 
-    def sendSetZone(self, zoneId):
-        self.air.sendSetZone(self, zoneId)
-
     def startMessageBundle(self, name):
     def startMessageBundle(self, name):
         self.air.startMessageBundle(name)
         self.air.startMessageBundle(name)
     def sendMessageBundle(self):
     def sendMessageBundle(self):
@@ -349,10 +340,10 @@ class DistributedObjectAI(DistributedObjectBase):
             self.air.sendUpdate(self, fieldName, args)
             self.air.sendUpdate(self, fieldName, args)
 
 
     def GetPuppetConnectionChannel(self, doId):
     def GetPuppetConnectionChannel(self, doId):
-        return doId + (1 << 32)
+        return doId + (1001 << 32)
 
 
     def GetAccountConnectionChannel(self, doId):
     def GetAccountConnectionChannel(self, doId):
-        return doId + (3 << 32)
+        return doId + (1003 << 32)
 
 
     def GetAccountIDFromChannelCode(self, channel):
     def GetAccountIDFromChannelCode(self, channel):
         return channel >> 32
         return channel >> 32
@@ -482,7 +473,6 @@ class DistributedObjectAI(DistributedObjectBase):
                 (self.__class__, doId))
                 (self.__class__, doId))
             return
             return
         self.air.requestDelete(self)
         self.air.requestDelete(self)
-        self.air.startTrackRequestDeletedDO(self)
         self._DOAI_requestedDelete = True
         self._DOAI_requestedDelete = True
 
 
     def taskName(self, taskString):
     def taskName(self, taskString):
@@ -581,3 +571,5 @@ class DistributedObjectAI(DistributedObjectBase):
         """ This is a no-op on the AI. """
         """ This is a no-op on the AI. """
         pass
         pass
 
 
+    def setAI(self, aiChannel):
+        self.air.setAI(self.doId, aiChannel)

+ 8 - 1
direct/src/distributed/DistributedObjectBase.py

@@ -1,4 +1,3 @@
-
 from direct.showbase.DirectObject import DirectObject
 from direct.showbase.DirectObject import DirectObject
 from direct.directnotify.DirectNotifyGlobal import directNotify
 from direct.directnotify.DirectNotifyGlobal import directNotify
 
 
@@ -93,3 +92,11 @@ class DistributedObjectBase(DirectObject):
 
 
     def hasParentingRules(self):
     def hasParentingRules(self):
         return self.dclass.getFieldByName('setParentingRules') != None
         return self.dclass.getFieldByName('setParentingRules') != None
+
+    def delete(self):
+        """
+        Override this to handle cleanup right before this object
+        gets deleted.
+        """
+
+        pass

+ 2 - 2
direct/src/distributed/DistributedObjectUD.py

@@ -270,10 +270,10 @@ class DistributedObjectUD(DistributedObjectBase):
             self.air.sendUpdate(self, fieldName, args)
             self.air.sendUpdate(self, fieldName, args)
 
 
     def GetPuppetConnectionChannel(self, doId):
     def GetPuppetConnectionChannel(self, doId):
-        return doId + (1 << 32)
+        return doId + (1001 << 32)
 
 
     def GetAccountConnectionChannel(self, doId):
     def GetAccountConnectionChannel(self, doId):
-        return doId + (3 << 32)
+        return doId + (1003 << 32)
 
 
     def GetAccountIDFromChannelCode(self, channel):
     def GetAccountIDFromChannelCode(self, channel):
         return channel >> 32
         return channel >> 32

+ 13 - 9
direct/src/distributed/DoInterestManager.py

@@ -111,7 +111,7 @@ class DoInterestManager(DirectObject.DirectObject):
         self._allInterestsCompleteCallbacks = []
         self._allInterestsCompleteCallbacks = []
 
 
     def __verbose(self):
     def __verbose(self):
-        return self.InterestDebug or self.getVerbose()
+        return self.InterestDebug.getValue() or self.getVerbose()
 
 
     def _getAnonymousEvent(self, desc):
     def _getAnonymousEvent(self, desc):
         return 'anonymous-%s-%s' % (desc, DoInterestManager._SerialGen.next())
         return 'anonymous-%s-%s' % (desc, DoInterestManager._SerialGen.next())
@@ -504,18 +504,23 @@ class DoInterestManager(DirectObject.DirectObject):
                 'trying to set interest to invalid parent: %s' % parentId)
                 'trying to set interest to invalid parent: %s' % parentId)
         datagram = PyDatagram()
         datagram = PyDatagram()
         # Add message type
         # Add message type
-        datagram.addUint16(CLIENT_ADD_INTEREST)
-        datagram.addUint16(handle)
-        datagram.addUint32(contextId)
-        datagram.addUint32(parentId)
         if isinstance(zoneIdList, list):
         if isinstance(zoneIdList, list):
             vzl = list(zoneIdList)
             vzl = list(zoneIdList)
             vzl.sort()
             vzl.sort()
             uniqueElements(vzl)
             uniqueElements(vzl)
+            datagram.addUint16(CLIENT_ADD_INTEREST_MULTIPLE)
+            datagram.addUint32(contextId)
+            datagram.addUint16(handle)
+            datagram.addUint32(parentId)
+            datagram.addUint16(len(vzl))
             for zone in vzl:
             for zone in vzl:
                 datagram.addUint32(zone)
                 datagram.addUint32(zone)
         else:
         else:
-           datagram.addUint32(zoneIdList)
+            datagram.addUint16(CLIENT_ADD_INTEREST)
+            datagram.addUint32(contextId)
+            datagram.addUint16(handle)
+            datagram.addUint32(parentId)
+            datagram.addUint32(zoneIdList)
         self.send(datagram)
         self.send(datagram)
 
 
     def _sendRemoveInterest(self, handle, contextId):
     def _sendRemoveInterest(self, handle, contextId):
@@ -530,9 +535,8 @@ class DoInterestManager(DirectObject.DirectObject):
         datagram = PyDatagram()
         datagram = PyDatagram()
         # Add message type
         # Add message type
         datagram.addUint16(CLIENT_REMOVE_INTEREST)
         datagram.addUint16(CLIENT_REMOVE_INTEREST)
+        datagram.addUint32(contextId)
         datagram.addUint16(handle)
         datagram.addUint16(handle)
-        if contextId != 0:
-            datagram.addUint32(contextId)
         self.send(datagram)
         self.send(datagram)
         if __debug__:
         if __debug__:
             state = DoInterestManager._interests[handle]
             state = DoInterestManager._interests[handle]
@@ -583,8 +587,8 @@ class DoInterestManager(DirectObject.DirectObject):
         This handles the interest done messages and may dispatch an event
         This handles the interest done messages and may dispatch an event
         """
         """
         assert DoInterestManager.notify.debugCall()
         assert DoInterestManager.notify.debugCall()
-        handle = di.getUint16()
         contextId = di.getUint32()
         contextId = di.getUint32()
+        handle = di.getUint16()
         if self.__verbose():
         if self.__verbose():
             print('CR::INTEREST.interestDone(handle=%s)' % handle)
             print('CR::INTEREST.interestDone(handle=%s)' % handle)
         DoInterestManager.notify.debug(
         DoInterestManager.notify.debug(

+ 133 - 97
direct/src/distributed/MsgTypes.py

@@ -3,104 +3,140 @@
 from direct.showbase.PythonUtil import invertDictLossless
 from direct.showbase.PythonUtil import invertDictLossless
 
 
 MsgName2Id = {
 MsgName2Id = {
-    # 2 new params: passwd, char bool 0/1 1 = new account
-    # 2 new return values: 129 = not found, 12 = bad passwd,
-    'CLIENT_LOGIN':                                  1,
-    'CLIENT_LOGIN_RESP':                             2,
-    'CLIENT_GET_AVATARS':                            3,
+    'CLIENT_HELLO':                                  1,
+    'CLIENT_HELLO_RESP':                             2,
+
+    # Sent by the client when it's leaving.
+    'CLIENT_DISCONNECT':                             3,
+
     # Sent by the server when it is dropping the connection deliberately.
     # Sent by the server when it is dropping the connection deliberately.
-    'CLIENT_GO_GET_LOST':                            4,
-    'CLIENT_GET_AVATARS_RESP':                       5,
-    'CLIENT_CREATE_AVATAR':                          6,
-    'CLIENT_CREATE_AVATAR_RESP':                     7,
-    'CLIENT_GET_FRIEND_LIST':                        10,
-    'CLIENT_GET_FRIEND_LIST_RESP':                   11,
-    'CLIENT_GET_AVATAR_DETAILS':                     14,
-    'CLIENT_GET_AVATAR_DETAILS_RESP':                15,
-    'CLIENT_LOGIN_2':                                16,
-    'CLIENT_LOGIN_2_RESP':                           17,
-
-    'CLIENT_OBJECT_UPDATE_FIELD':                    24,
-    'CLIENT_OBJECT_UPDATE_FIELD_RESP':               24,
-    'CLIENT_OBJECT_DISABLE':                         25,
-    'CLIENT_OBJECT_DISABLE_RESP':                    25,
-    'CLIENT_OBJECT_DISABLE_OWNER':                   26,
-    'CLIENT_OBJECT_DISABLE_OWNER_RESP':              26,
-    'CLIENT_OBJECT_DELETE':                          27,
-    'CLIENT_OBJECT_DELETE_RESP':                     27,
-    'CLIENT_SET_ZONE_CMU':                           29,
-    'CLIENT_REMOVE_ZONE':                            30,
-    'CLIENT_SET_AVATAR':                             32,
-    'CLIENT_CREATE_OBJECT_REQUIRED':                 34,
-    'CLIENT_CREATE_OBJECT_REQUIRED_RESP':            34,
-    'CLIENT_CREATE_OBJECT_REQUIRED_OTHER':           35,
-    'CLIENT_CREATE_OBJECT_REQUIRED_OTHER_RESP':      35,
-    'CLIENT_CREATE_OBJECT_REQUIRED_OTHER_OWNER':     36,
-    'CLIENT_CREATE_OBJECT_REQUIRED_OTHER_OWNER_RESP':36,
-
-    'CLIENT_REQUEST_GENERATES':                      36,
-
-    'CLIENT_DISCONNECT':                             37,
-
-    'CLIENT_GET_STATE_RESP':                         47,
-    'CLIENT_DONE_INTEREST_RESP':                     48,
-
-    'CLIENT_DELETE_AVATAR':                          49,
-
-    'CLIENT_DELETE_AVATAR_RESP':                     5,
-
-    'CLIENT_HEARTBEAT':                              52,
-    'CLIENT_FRIEND_ONLINE':                          53,
-    'CLIENT_FRIEND_OFFLINE':                         54,
-    'CLIENT_REMOVE_FRIEND':                          56,
-
-    'CLIENT_CHANGE_PASSWORD':                        65,
-
-    'CLIENT_SET_NAME_PATTERN':                       67,
-    'CLIENT_SET_NAME_PATTERN_ANSWER':                68,
-
-    'CLIENT_SET_WISHNAME':                           70,
-    'CLIENT_SET_WISHNAME_RESP':                      71,
-    'CLIENT_SET_WISHNAME_CLEAR':                     72,
-    'CLIENT_SET_SECURITY':                           73,
-
-    'CLIENT_SET_DOID_RANGE':                         74,
-
-    'CLIENT_GET_AVATARS_RESP2':                      75,
-    'CLIENT_CREATE_AVATAR2':                         76,
-    'CLIENT_SYSTEM_MESSAGE':                         78,
-    'CLIENT_SET_AVTYPE':                             80,
-
-    'CLIENT_GET_PET_DETAILS':                        81,
-    'CLIENT_GET_PET_DETAILS_RESP':                   82,
-
-    'CLIENT_ADD_INTEREST':                           97,
-    'CLIENT_REMOVE_INTEREST':                        99,
-    'CLIENT_OBJECT_LOCATION':                        102,
-
-    'CLIENT_LOGIN_3':                                111,
-    'CLIENT_LOGIN_3_RESP':                           110,
-
-    'CLIENT_GET_FRIEND_LIST_EXTENDED':               115,
-    'CLIENT_GET_FRIEND_LIST_EXTENDED_RESP':          116,
-
-    'CLIENT_SET_FIELD_SENDABLE':                     120,
-
-    'CLIENT_SYSTEMMESSAGE_AKNOWLEDGE':               123,
-    'CLIENT_CHANGE_GENERATE_ORDER':                  124,
-
-    # new toontown specific login message, adds last logged in, and if child account has parent acount
-    'CLIENT_LOGIN_TOONTOWN':                         125,
-    'CLIENT_LOGIN_TOONTOWN_RESP':                    126,
-
-
-
-    'STATESERVER_OBJECT_GENERATE_WITH_REQUIRED':           2001,
-    'STATESERVER_OBJECT_GENERATE_WITH_REQUIRED_OTHER':     2003,
-    'STATESERVER_OBJECT_UPDATE_FIELD':                     2004,
-    'STATESERVER_OBJECT_CREATE_WITH_REQUIRED_CONTEXT':     2050,
-    'STATESERVER_OBJECT_CREATE_WITH_REQUIR_OTHER_CONTEXT': 2051,
-    'STATESERVER_BOUNCE_MESSAGE':                          2086,
+    'CLIENT_EJECT':                                  4,
+
+    'CLIENT_HEARTBEAT':                              5,
+
+    'CLIENT_OBJECT_SET_FIELD':                       120,
+    'CLIENT_OBJECT_SET_FIELDS':                      121,
+    'CLIENT_OBJECT_LEAVING':                         132,
+    'CLIENT_OBJECT_LEAVING_OWNER':                   161,
+    'CLIENT_ENTER_OBJECT_REQUIRED':                  142,
+    'CLIENT_ENTER_OBJECT_REQUIRED_OTHER':            143,
+    'CLIENT_ENTER_OBJECT_REQUIRED_OWNER':            172,
+    'CLIENT_ENTER_OBJECT_REQUIRED_OTHER_OWNER':      173,
+
+    'CLIENT_DONE_INTEREST_RESP':                     204,
+
+    'CLIENT_ADD_INTEREST':                           200,
+    'CLIENT_ADD_INTEREST_MULTIPLE':                  201,
+    'CLIENT_REMOVE_INTEREST':                        203,
+    'CLIENT_OBJECT_LOCATION':                        140,
+
+
+    # These are sent internally inside the Astron cluster.
+
+    # Message Director control messages:
+    'CONTROL_CHANNEL':                                  1,
+    'CONTROL_ADD_CHANNEL':                              9000,
+    'CONTROL_REMOVE_CHANNEL':                           9001,
+    'CONTROL_ADD_RANGE':                                9002,
+    'CONTROL_REMOVE_RANGE':                             9003,
+    'CONTROL_ADD_POST_REMOVE':                          9010,
+    'CONTROL_CLEAR_POST_REMOVES':                       9011,
+
+    # State Server control messages:
+    'STATESERVER_CREATE_OBJECT_WITH_REQUIRED':          2000,
+    'STATESERVER_CREATE_OBJECT_WITH_REQUIRED_OTHER':    2001,
+    'STATESERVER_DELETE_AI_OBJECTS':                    2009,
+    'STATESERVER_OBJECT_GET_FIELD':                     2010,
+    'STATESERVER_OBJECT_GET_FIELD_RESP':                2011,
+    'STATESERVER_OBJECT_GET_FIELDS':                    2012,
+    'STATESERVER_OBJECT_GET_FIELDS_RESP':               2013,
+    'STATESERVER_OBJECT_GET_ALL':                       2014,
+    'STATESERVER_OBJECT_GET_ALL_RESP':                  2015,
+    'STATESERVER_OBJECT_SET_FIELD':                     2020,
+    'STATESERVER_OBJECT_SET_FIELDS':                    2021,
+    'STATESERVER_OBJECT_DELETE_FIELD_RAM':              2030,
+    'STATESERVER_OBJECT_DELETE_FIELDS_RAM':             2031,
+    'STATESERVER_OBJECT_DELETE_RAM':                    2032,
+    'STATESERVER_OBJECT_SET_LOCATION':                          2040,
+    'STATESERVER_OBJECT_CHANGING_LOCATION':                     2041,
+    'STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED':          2042,
+    'STATESERVER_OBJECT_ENTER_LOCATION_WITH_REQUIRED_OTHER':    2043,
+    'STATESERVER_OBJECT_GET_LOCATION':                          2044,
+    'STATESERVER_OBJECT_GET_LOCATION_RESP':                     2045,
+    'STATESERVER_OBJECT_SET_AI':                                2050,
+    'STATESERVER_OBJECT_CHANGING_AI':                           2051,
+    'STATESERVER_OBJECT_ENTER_AI_WITH_REQUIRED':                2052,
+    'STATESERVER_OBJECT_ENTER_AI_WITH_REQUIRED_OTHER':          2053,
+    'STATESERVER_OBJECT_GET_AI':                                2054,
+    'STATESERVER_OBJECT_GET_AI_RESP':                           2055,
+    'STATESERVER_OBJECT_SET_OWNER':                             2060,
+    'STATESERVER_OBJECT_CHANGING_OWNER':                        2061,
+    'STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED':             2062,
+    'STATESERVER_OBJECT_ENTER_OWNER_WITH_REQUIRED_OTHER':       2063,
+    'STATESERVER_OBJECT_GET_OWNER':                             2064,
+    'STATESERVER_OBJECT_GET_OWNER_RESP':                        2065,
+    'STATESERVER_OBJECT_GET_ZONE_OBJECTS':              2100,
+    'STATESERVER_OBJECT_GET_ZONES_OBJECTS':             2102,
+    'STATESERVER_OBJECT_GET_CHILDREN':                  2104,
+    'STATESERVER_OBJECT_GET_ZONE_COUNT':                2110,
+    'STATESERVER_OBJECT_GET_ZONE_COUNT_RESP':           2111,
+    'STATESERVER_OBJECT_GET_ZONES_COUNT':               2112,
+    'STATESERVER_OBJECT_GET_ZONES_COUNT_RESP':          2113,
+    'STATESERVER_OBJECT_GET_CHILD_COUNT':               2114,
+    'STATESERVER_OBJECT_GET_CHILD_COUNT_RESP':          2115,
+    'STATESERVER_OBJECT_DELETE_ZONE':                   2120,
+    'STATESERVER_OBJECT_DELETE_ZONES':                  2122,
+    'STATESERVER_OBJECT_DELETE_CHILDREN':               2124,
+    # DBSS-backed-object messages:
+    'DBSS_OBJECT_ACTIVATE_WITH_DEFAULTS':        2200,
+    'DBSS_OBJECT_ACTIVATE_WITH_DEFAULTS_OTHER':  2201,
+    'DBSS_OBJECT_GET_ACTIVATED':                 2207,
+    'DBSS_OBJECT_GET_ACTIVATED_RESP':            2208,
+    'DBSS_OBJECT_DELETE_FIELD_DISK':             2230,
+    'DBSS_OBJECT_DELETE_FIELDS_DISK':            2231,
+    'DBSS_OBJECT_DELETE_DISK':                   2232,
+
+    # Database Server control messages:
+    'DBSERVER_CREATE_OBJECT':                       3000,
+    'DBSERVER_CREATE_OBJECT_RESP':                  3001,
+    'DBSERVER_OBJECT_GET_FIELD':                    3010,
+    'DBSERVER_OBJECT_GET_FIELD_RESP':               3011,
+    'DBSERVER_OBJECT_GET_FIELDS':                   3012,
+    'DBSERVER_OBJECT_GET_FIELDS_RESP':              3013,
+    'DBSERVER_OBJECT_GET_ALL':                      3014,
+    'DBSERVER_OBJECT_GET_ALL_RESP':                 3015,
+    'DBSERVER_OBJECT_SET_FIELD':                    3020,
+    'DBSERVER_OBJECT_SET_FIELDS':                   3021,
+    'DBSERVER_OBJECT_SET_FIELD_IF_EQUALS':          3022,
+    'DBSERVER_OBJECT_SET_FIELD_IF_EQUALS_RESP':     3023,
+    'DBSERVER_OBJECT_SET_FIELDS_IF_EQUALS':         3024,
+    'DBSERVER_OBJECT_SET_FIELDS_IF_EQUALS_RESP':    3025,
+    'DBSERVER_OBJECT_SET_FIELD_IF_EMPTY':           3026,
+    'DBSERVER_OBJECT_SET_FIELD_IF_EMPTY_RESP':      3027,
+    'DBSERVER_OBJECT_DELETE_FIELD':                 3030,
+    'DBSERVER_OBJECT_DELETE_FIELDS':                3031,
+    'DBSERVER_OBJECT_DELETE':                       3032,
+
+    # Client Agent control messages:
+    'CLIENTAGENT_SET_STATE':                        1000,
+    'CLIENTAGENT_SET_CLIENT_ID':                    1001,
+    'CLIENTAGENT_SEND_DATAGRAM':                    1002,
+    'CLIENTAGENT_EJECT':                            1004,
+    'CLIENTAGENT_DROP':                             1005,
+    'CLIENTAGENT_GET_NETWORK_ADDRESS':              1006,
+    'CLIENTAGENT_GET_NETWORK_ADDRESS_RESP':         1007,
+    'CLIENTAGENT_DECLARE_OBJECT':                   1010,
+    'CLIENTAGENT_UNDECLARE_OBJECT':                 1011,
+    'CLIENTAGENT_ADD_SESSION_OBJECT':               1012,
+    'CLIENTAGENT_REMOVE_SESSION_OBJECT':            1013,
+    'CLIENTAGENT_SET_FIELDS_SENDABLE':              1014,
+    'CLIENTAGENT_OPEN_CHANNEL':                     1100,
+    'CLIENTAGENT_CLOSE_CHANNEL':                    1101,
+    'CLIENTAGENT_ADD_POST_REMOVE':                  1110,
+    'CLIENTAGENT_CLEAR_POST_REMOVES':               1111,
+    'CLIENTAGENT_ADD_INTEREST':                     1200,
+    'CLIENTAGENT_ADD_INTEREST_MULTIPLE':            1201,
+    'CLIENTAGENT_REMOVE_INTEREST':                  1203,
     }
     }
 
 
 # create id->name table for debugging
 # create id->name table for debugging

+ 1 - 1
direct/src/distributed/MsgTypesCMU.py

@@ -19,7 +19,7 @@ MsgName2Id = {
     'CLIENT_HEARTBEAT_CMU'                    : 9011,
     'CLIENT_HEARTBEAT_CMU'                    : 9011,
     'CLIENT_OBJECT_UPDATE_FIELD_TARGETED_CMU'  : 9011,
     'CLIENT_OBJECT_UPDATE_FIELD_TARGETED_CMU'  : 9011,
 
 
-    'CLIENT_OBJECT_UPDATE_FIELD' : 24,  # Matches MsgTypes.CLIENT_OBJECT_UPDATE_FIELD
+    'CLIENT_OBJECT_UPDATE_FIELD' : 120,  # Matches MsgTypes.CLIENT_OBJECT_SET_FIELD
     }
     }
 
 
 # create id->name table for debugging
 # create id->name table for debugging

+ 0 - 208
direct/src/distributed/OldClientRepository.py

@@ -1,208 +0,0 @@
-"""OldClientRepository module: contains the OldClientRepository class"""
-
-from .ClientRepositoryBase import *
-
-class OldClientRepository(ClientRepositoryBase):
-    """
-    This is the open-source ClientRepository as provided by CMU.  It
-    communicates with the ServerRepository in this same directory.
-
-    If you are looking for the VR Studio's implementation of the
-    client repository, look to OTPClientRepository (elsewhere).
-    """
-    notify = DirectNotifyGlobal.directNotify.newCategory("ClientRepository")
-
-    def __init__(self, dcFileNames = None):
-        ClientRepositoryBase.__init__(self, dcFileNames = dcFileNames)
-
-        # The DOID allocator.  The CMU LAN server may choose to
-        # send us a block of DOIDs.  If it chooses to do so, then we
-        # may create objects, using those DOIDs.
-        self.DOIDbase = 0
-        self.DOIDnext = 0
-        self.DOIDlast = 0
-
-    def handleSetDOIDrange(self, di):
-        self.DOIDbase = di.getUint32()
-        self.DOIDlast = self.DOIDbase + di.getUint32()
-        self.DOIDnext = self.DOIDbase
-
-    def handleRequestGenerates(self, di):
-        # When new clients join the zone of an object, they need to hear
-        # about it, so we send out all of our information about objects in
-        # that particular zone.
-
-        assert self.DOIDnext < self.DOIDlast
-        zone = di.getUint32()
-        for obj in self.doId2do.values():
-            if obj.zone == zone:
-                id = obj.doId
-                if (self.isLocalId(id)):
-                    self.send(obj.dclass.clientFormatGenerate(obj, id, zone, []))
-
-    def createWithRequired(self, className, zoneId = 0, optionalFields=None):
-        if self.DOIDnext >= self.DOIDlast:
-            self.notify.error(
-                "Cannot allocate a distributed object ID: all IDs used up.")
-            return None
-        id = self.DOIDnext
-        self.DOIDnext = self.DOIDnext + 1
-        dclass = self.dclassesByName[className]
-        classDef = dclass.getClassDef()
-        if classDef == None:
-            self.notify.error("Could not create an undefined %s object." % (
-                dclass.getName()))
-        obj = classDef(self)
-        obj.dclass = dclass
-        obj.zone = zoneId
-        obj.doId = id
-        self.doId2do[id] = obj
-        obj.generateInit()
-        obj._retrieveCachedData()
-        obj.generate()
-        obj.announceGenerate()
-        datagram = dclass.clientFormatGenerate(obj, id, zoneId, optionalFields)
-        self.send(datagram)
-        return obj
-
-    def sendDisableMsg(self, doId):
-        datagram = PyDatagram()
-        datagram.addUint16(CLIENT_OBJECT_DISABLE)
-        datagram.addUint32(doId)
-        self.send(datagram)
-
-    def sendDeleteMsg(self, doId):
-        datagram = PyDatagram()
-        datagram.addUint16(CLIENT_OBJECT_DELETE)
-        datagram.addUint32(doId)
-        self.send(datagram)
-
-    def sendRemoveZoneMsg(self, zoneId, visibleZoneList=None):
-        datagram = PyDatagram()
-        datagram.addUint16(CLIENT_REMOVE_ZONE)
-        datagram.addUint32(zoneId)
-
-        # if we have an explicit list of visible zones, add them
-        if visibleZoneList is not None:
-            vzl = list(visibleZoneList)
-            vzl.sort()
-            assert PythonUtil.uniqueElements(vzl)
-            for zone in vzl:
-                datagram.addUint32(zone)
-
-        # send the message
-        self.send(datagram)
-
-    def sendUpdateZone(self, obj, zoneId):
-        id = obj.doId
-        assert self.isLocalId(id)
-        self.sendDeleteMsg(id, 1)
-        obj.zone = zoneId
-        self.send(obj.dclass.clientFormatGenerate(obj, id, zoneId, []))
-
-    def sendSetZoneMsg(self, zoneId, visibleZoneList=None):
-        datagram = PyDatagram()
-        # Add message type
-        datagram.addUint16(CLIENT_SET_ZONE_CMU)
-        # Add zone id
-        datagram.addUint32(zoneId)
-
-        # if we have an explicit list of visible zones, add them
-        if visibleZoneList is not None:
-            vzl = list(visibleZoneList)
-            vzl.sort()
-            assert PythonUtil.uniqueElements(vzl)
-            for zone in vzl:
-                datagram.addUint32(zone)
-
-        # send the message
-        self.send(datagram)
-
-    def isLocalId(self, id):
-        return ((id >= self.DOIDbase) and (id < self.DOIDlast))
-
-    def haveCreateAuthority(self):
-        return (self.DOIDlast > self.DOIDnext)
-
-    def handleDatagram(self, di):
-        if self.notify.getDebug():
-            print("ClientRepository received datagram:")
-            di.getDatagram().dumpHex(ostream)
-
-        msgType = self.getMsgType()
-
-        # These are the sort of messages we may expect from the public
-        # Panda server.
-
-        if msgType == CLIENT_SET_DOID_RANGE:
-            self.handleSetDOIDrange(di)
-        elif msgType == CLIENT_CREATE_OBJECT_REQUIRED_RESP:
-            self.handleGenerateWithRequired(di)
-        elif msgType == CLIENT_CREATE_OBJECT_REQUIRED_OTHER_RESP:
-            self.handleGenerateWithRequiredOther(di)
-        elif msgType == CLIENT_OBJECT_UPDATE_FIELD_RESP:
-            self.handleUpdateField(di)
-        elif msgType == CLIENT_OBJECT_DELETE_RESP:
-            self.handleDelete(di)
-        elif msgType == CLIENT_OBJECT_DISABLE_RESP:
-            self.handleDisable(di)
-        elif msgType == CLIENT_REQUEST_GENERATES:
-            self.handleRequestGenerates(di)
-        else:
-            self.handleMessageType(msgType, di)
-
-        # If we're processing a lot of datagrams within one frame, we
-        # may forget to send heartbeats.  Keep them coming!
-        self.considerHeartbeat()
-
-    def handleGenerateWithRequired(self, di):
-        # Get the class Id
-        classId = di.getUint16()
-        # Get the DO Id
-        doId = di.getUint32()
-        # Look up the dclass
-        dclass = self.dclassesByNumber[classId]
-        dclass.startGenerate()
-        # Create a new distributed object, and put it in the dictionary
-        distObj = self.generateWithRequiredFields(dclass, doId, di)
-        dclass.stopGenerate()
-
-    def generateWithRequiredFields(self, dclass, doId, di):
-        if doId in self.doId2do:
-            # ...it is in our dictionary.
-            # Just update it.
-            distObj = self.doId2do[doId]
-            assert distObj.dclass == dclass
-            distObj.generate()
-            distObj.updateRequiredFields(dclass, di)
-            # updateRequiredFields calls announceGenerate
-        elif self.cache.contains(doId):
-            # ...it is in the cache.
-            # Pull it out of the cache:
-            distObj = self.cache.retrieve(doId)
-            assert distObj.dclass == dclass
-            # put it in the dictionary:
-            self.doId2do[doId] = distObj
-            # and update it.
-            distObj.generate()
-            distObj.updateRequiredFields(dclass, di)
-            # updateRequiredFields calls announceGenerate
-        else:
-            # ...it is not in the dictionary or the cache.
-            # Construct a new one
-            classDef = dclass.getClassDef()
-            if classDef == None:
-                self.notify.error("Could not create an undefined %s object." % (
-                    dclass.getName()))
-            distObj = classDef(self)
-            distObj.dclass = dclass
-            # Assign it an Id
-            distObj.doId = doId
-            # Put the new do in the dictionary
-            self.doId2do[doId] = distObj
-            # Update the required fields
-            distObj.generateInit()  # Only called when constructed
-            distObj.generate()
-            distObj.updateRequiredFields(dclass, di)
-            # updateRequiredFields calls announceGenerate
-        return distObj

+ 5 - 8
direct/src/distributed/PyDatagram.py

@@ -7,7 +7,7 @@ from panda3d.core import Datagram
 from panda3d.direct import *
 from panda3d.direct import *
 # Import the type numbers
 # Import the type numbers
 
 
-#from otp.ai.AIMsgTypes import *
+from direct.distributed.MsgTypes import *
 
 
 class PyDatagram(Datagram):
 class PyDatagram(Datagram):
 
 
@@ -47,13 +47,10 @@ class PyDatagram(Datagram):
         self.addUint16(code)
         self.addUint16(code)
 
 
 
 
-#    def addServerControlHeader(self,   code):
-#        self.addInt8(1)
-#        self.addChannel(CONTROL_MESSAGE)
-#        self.addUint16(code)
-#    def addOldServerControlHeader(self,   code):
-#        self.addChannel(CONTROL_MESSAGE)
-#        self.addUint16(code)
+    def addServerControlHeader(self, code):
+        self.addInt8(1)
+        self.addChannel(CONTROL_CHANNEL)
+        self.addUint16(code)
 
 
     def putArg(self, arg, subatomicType, divisor=1):
     def putArg(self, arg, subatomicType, divisor=1):
         if (divisor == 1):
         if (divisor == 1):

+ 9 - 9
direct/src/distributed/cConnectionRepository.cxx

@@ -301,8 +301,8 @@ check_datagram() {
 
 
     switch (_msg_type) {
     switch (_msg_type) {
 #ifdef HAVE_PYTHON
 #ifdef HAVE_PYTHON
-    case CLIENT_OBJECT_UPDATE_FIELD:
-    case STATESERVER_OBJECT_UPDATE_FIELD:
+    case CLIENT_OBJECT_SET_FIELD:
+    case STATESERVER_OBJECT_SET_FIELD:
       if (_handle_c_updates) {
       if (_handle_c_updates) {
         if (_has_owner_view) {
         if (_has_owner_view) {
           if (!handle_update_field_owner()) {
           if (!handle_update_field_owner()) {
@@ -494,7 +494,7 @@ send_message_bundle(unsigned int channel, unsigned int sender_channel) {
     dg.add_int8(1);
     dg.add_int8(1);
     dg.add_uint64(channel);
     dg.add_uint64(channel);
     dg.add_uint64(sender_channel);
     dg.add_uint64(sender_channel);
-    dg.add_uint16(STATESERVER_BOUNCE_MESSAGE);
+    //dg.add_uint16(STATESERVER_BOUNCE_MESSAGE);
     // add each bundled message
     // add each bundled message
     BundledMsgVector::const_iterator bmi;
     BundledMsgVector::const_iterator bmi;
     for (bmi = _bundle_msgs.begin(); bmi != _bundle_msgs.end(); bmi++) {
     for (bmi = _bundle_msgs.begin(); bmi != _bundle_msgs.end(); bmi++) {
@@ -708,7 +708,7 @@ handle_update_field() {
       Py_DECREF(dclass_obj);
       Py_DECREF(dclass_obj);
       nassertr(dclass_this != NULL, false);
       nassertr(dclass_this != NULL, false);
 
 
-      DCClass *dclass = (DCClass *)PyLong_AsLong(dclass_this);
+      DCClass *dclass = (DCClass *)PyLong_AsVoidPtr(dclass_this);
       Py_DECREF(dclass_this);
       Py_DECREF(dclass_this);
 
 
       // If in quiet zone mode, throw update away unless distobj has
       // If in quiet zone mode, throw update away unless distobj has
@@ -799,7 +799,7 @@ handle_update_field_owner() {
       Py_DECREF(dclass_obj);
       Py_DECREF(dclass_obj);
       nassertr(dclass_this != NULL, false);
       nassertr(dclass_this != NULL, false);
 
 
-      DCClass *dclass = (DCClass *)PyLong_AsLong(dclass_this);
+      DCClass *dclass = (DCClass *)PyLong_AsVoidPtr(dclass_this);
       Py_DECREF(dclass_this);
       Py_DECREF(dclass_this);
 
 
       // check if we should forward this update to the owner view
       // check if we should forward this update to the owner view
@@ -841,7 +841,7 @@ handle_update_field_owner() {
       Py_DECREF(dclass_obj);
       Py_DECREF(dclass_obj);
       nassertr(dclass_this != NULL, false);
       nassertr(dclass_this != NULL, false);
 
 
-      DCClass *dclass = (DCClass *)PyLong_AsLong(dclass_this);
+      DCClass *dclass = (DCClass *)PyLong_AsVoidPtr(dclass_this);
       Py_DECREF(dclass_this);
       Py_DECREF(dclass_this);
 
 
       // check if we should forward this update to the owner view
       // check if we should forward this update to the owner view
@@ -899,11 +899,11 @@ describe_message(ostream &out, const string &prefix,
 
 
     packer.RAW_UNPACK_CHANNEL();  // msg_sender
     packer.RAW_UNPACK_CHANNEL();  // msg_sender
     msg_type = packer.raw_unpack_uint16();
     msg_type = packer.raw_unpack_uint16();
-    is_update = (msg_type == STATESERVER_OBJECT_UPDATE_FIELD);
+    is_update = (msg_type == STATESERVER_OBJECT_SET_FIELD);
 
 
   } else {
   } else {
     msg_type = packer.raw_unpack_uint16();
     msg_type = packer.raw_unpack_uint16();
-    is_update = (msg_type == CLIENT_OBJECT_UPDATE_FIELD);
+    is_update = (msg_type == CLIENT_OBJECT_SET_FIELD);
   }
   }
 
 
   if (!is_update) {
   if (!is_update) {
@@ -974,7 +974,7 @@ describe_message(ostream &out, const string &prefix,
         Py_DECREF(dclass_obj);
         Py_DECREF(dclass_obj);
         nassertv(dclass_this != NULL);
         nassertv(dclass_this != NULL);
 
 
-        dclass = (DCClass *)PyLong_AsLong(dclass_this);
+        dclass = (DCClass *)PyLong_AsVoidPtr(dclass_this);
         Py_DECREF(dclass_this);
         Py_DECREF(dclass_this);
       }
       }
     }
     }

+ 2 - 2
direct/src/distributed/cDistributedSmoothNodeBase.cxx

@@ -278,12 +278,12 @@ begin_send_update(DCPacker &packer, const string &field_name) {
     packer.RAW_PACK_CHANNEL(_do_id);
     packer.RAW_PACK_CHANNEL(_do_id);
     packer.RAW_PACK_CHANNEL(_ai_id);
     packer.RAW_PACK_CHANNEL(_ai_id);
     // packer.raw_pack_uint8('A');
     // packer.raw_pack_uint8('A');
-    packer.raw_pack_uint16(STATESERVER_OBJECT_UPDATE_FIELD);
+    packer.raw_pack_uint16(STATESERVER_OBJECT_SET_FIELD);
     packer.raw_pack_uint32(_do_id);
     packer.raw_pack_uint32(_do_id);
     packer.raw_pack_uint16(field->get_number());
     packer.raw_pack_uint16(field->get_number());
 
 
   } else {
   } else {
-    packer.raw_pack_uint16(CLIENT_OBJECT_UPDATE_FIELD);
+    packer.raw_pack_uint16(CLIENT_OBJECT_SET_FIELD);
     packer.raw_pack_uint32(_do_id);
     packer.raw_pack_uint32(_do_id);
     packer.raw_pack_uint16(field->get_number());
     packer.raw_pack_uint16(field->get_number());
   }
   }

+ 7 - 1
direct/src/filter/CommonFilters.py

@@ -132,6 +132,9 @@ class CommonFilters:
             if (len(configuration) == 0):
             if (len(configuration) == 0):
                 return
                 return
 
 
+            if not self.manager.win.gsg.getSupportsBasicShaders():
+                return False
+
             auxbits = 0
             auxbits = 0
             needtex = set(["color"])
             needtex = set(["color"])
             needtexcoord = set(["color"])
             needtexcoord = set(["color"])
@@ -339,7 +342,10 @@ class CommonFilters:
                 text += "  o_color = float4(1, 1, 1, 1) - o_color;\n"
                 text += "  o_color = float4(1, 1, 1, 1) - o_color;\n"
             text += "}\n"
             text += "}\n"
 
 
-            self.finalQuad.setShader(Shader.make(text, Shader.SL_Cg))
+            shader = Shader.make(text, Shader.SL_Cg)
+            if not shader:
+                return False
+            self.finalQuad.setShader(shader)
             for tex in self.textures:
             for tex in self.textures:
                 self.finalQuad.setShaderInput("tx"+tex, self.textures[tex])
                 self.finalQuad.setShaderInput("tx"+tex, self.textures[tex])
 
 

+ 5 - 0
direct/src/gui/DirectEntry.py

@@ -59,6 +59,8 @@ class DirectEntry(DirectFrame):
             # Text used for the PGEntry text node
             # Text used for the PGEntry text node
             # NOTE: This overrides the DirectFrame text option
             # NOTE: This overrides the DirectFrame text option
             ('initialText',     '',               DGG.INITOPT),
             ('initialText',     '',               DGG.INITOPT),
+            # Enable or disable text overflow scrolling
+            ('overflow',        0,                self.setOverflowMode),
             # Command to be called on hitting Enter
             # Command to be called on hitting Enter
             ('command',        None,              None),
             ('command',        None,              None),
             ('extraArgs',      [],                None),
             ('extraArgs',      [],                None),
@@ -159,6 +161,9 @@ class DirectEntry(DirectFrame):
     def setCursorKeysActive(self):
     def setCursorKeysActive(self):
         PGEntry.setCursorKeysActive(self.guiItem, self['cursorKeys'])
         PGEntry.setCursorKeysActive(self.guiItem, self['cursorKeys'])
 
 
+    def setOverflowMode(self):
+        PGEntry.set_overflow_mode(self.guiItem, self['overflow'])
+
     def setObscureMode(self):
     def setObscureMode(self):
         PGEntry.setObscureMode(self.guiItem, self['obscured'])
         PGEntry.setObscureMode(self.guiItem, self['obscured'])
 
 

+ 22 - 23
direct/src/gui/DirectGuiBase.py

@@ -80,7 +80,8 @@ __all__ = ['DirectGuiBase', 'DirectGuiWidget']
 
 
 
 
 from panda3d.core import *
 from panda3d.core import *
-from panda3d.direct import get_config_showbase
+from direct.showbase import ShowBaseGlobal
+from direct.showbase.ShowBase import ShowBase
 from . import DirectGuiGlobals as DGG
 from . import DirectGuiGlobals as DGG
 from .OnscreenText import *
 from .OnscreenText import *
 from .OnscreenGeom import *
 from .OnscreenGeom import *
@@ -633,7 +634,7 @@ class DirectGuiBase(DirectObject.DirectObject):
         """
         """
         # Need to tack on gui item specific id
         # Need to tack on gui item specific id
         gEvent = event + self.guiId
         gEvent = event + self.guiId
-        if get_config_showbase().GetBool('debug-directgui-msgs', False):
+        if ShowBase.config.GetBool('debug-directgui-msgs', False):
             from direct.showbase.PythonUtil import StackTrace
             from direct.showbase.PythonUtil import StackTrace
             print(gEvent)
             print(gEvent)
             print(StackTrace())
             print(StackTrace())
@@ -662,7 +663,7 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
     # Determine the default initial state for inactive (or
     # Determine the default initial state for inactive (or
     # unclickable) components.  If we are in edit mode, these are
     # unclickable) components.  If we are in edit mode, these are
     # actually clickable by default.
     # actually clickable by default.
-    guiEdit = get_config_showbase().GetBool('direct-gui-edit', 0)
+    guiEdit = ShowBase.config.GetBool('direct-gui-edit', False)
     if guiEdit:
     if guiEdit:
         inactiveInitState = DGG.NORMAL
         inactiveInitState = DGG.NORMAL
     else:
     else:
@@ -723,21 +724,24 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
         if self['guiId']:
         if self['guiId']:
             self.guiItem.setId(self['guiId'])
             self.guiItem.setId(self['guiId'])
         self.guiId = self.guiItem.getId()
         self.guiId = self.guiItem.getId()
-        if __dev__:
+
+        if ShowBaseGlobal.__dev__:
             guiObjectCollector.addLevel(1)
             guiObjectCollector.addLevel(1)
             guiObjectCollector.flushLevel()
             guiObjectCollector.flushLevel()
             # track gui items by guiId for tracking down leaks
             # track gui items by guiId for tracking down leaks
-            if hasattr(base, 'guiItems'):
-                if self.guiId in base.guiItems:
-                    base.notify.warning('duplicate guiId: %s (%s stomping %s)' %
-                                        (self.guiId, self,
-                                         base.guiItems[self.guiId]))
-                base.guiItems[self.guiId] = self
-                if hasattr(base, 'printGuiCreates'):
-                    printStack()
+            if ShowBase.config.GetBool('track-gui-items', False):
+                if not hasattr(ShowBase, 'guiItems'):
+                    ShowBase.guiItems = {}
+                if self.guiId in ShowBase.guiItems:
+                    ShowBase.notify.warning('duplicate guiId: %s (%s stomping %s)' %
+                                            (self.guiId, self,
+                                             ShowBase.guiItems[self.guiId]))
+                ShowBase.guiItems[self.guiId] = self
+
         # Attach button to parent and make that self
         # Attach button to parent and make that self
-        if (parent == None):
-            parent = aspect2d
+        if parent is None:
+            parent = ShowBaseGlobal.aspect2d
+
         self.assign(parent.attachNewNode(self.guiItem, self['sortOrder']))
         self.assign(parent.attachNewNode(self.guiItem, self['sortOrder']))
         # Update pose to initial values
         # Update pose to initial values
         if self['pos']:
         if self['pos']:
@@ -1024,17 +1028,12 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
 
 
     def destroy(self):
     def destroy(self):
         if hasattr(self, "frameStyle"):
         if hasattr(self, "frameStyle"):
-            if __dev__:
+            if ShowBaseGlobal.__dev__:
                 guiObjectCollector.subLevel(1)
                 guiObjectCollector.subLevel(1)
                 guiObjectCollector.flushLevel()
                 guiObjectCollector.flushLevel()
-                if hasattr(base, 'guiItems'):
-                    if self.guiId in base.guiItems:
-                        del base.guiItems[self.guiId]
-                    else:
-                        base.notify.warning(
-                            'DirectGuiWidget.destroy(): '
-                            'gui item %s not in base.guiItems' %
-                            self.guiId)
+                if hasattr(ShowBase, 'guiItems'):
+                    ShowBase.guiItems.pop(self.guiId, None)
+
             # Destroy children
             # Destroy children
             for child in self.getChildren():
             for child in self.getChildren():
                 childGui = self.guiDict.get(child.getName())
                 childGui = self.guiDict.get(child.getName())

+ 2 - 2
direct/src/plugin_installer/FileAssociation.nsh

@@ -201,13 +201,13 @@ def arbitraryShadow(node):
 ##b.reparentTo((base.localAvatar))
 ##b.reparentTo((base.localAvatar))
 ##a = AmbientLight('cloudAmbientHi')
 ##a = AmbientLight('cloudAmbientHi')
 ##a.setColor(Vec4(0.9, 0.9, 0.9, 1.000))
 ##a.setColor(Vec4(0.9, 0.9, 0.9, 1.000))
-##aNP = s.attachNewNode(a.upcastToPandaNode())
+##aNP = s.attachNewNode(a)
 ##b.setLight(aNP)
 ##b.setLight(aNP)
 ##d = DirectionalLight("chernabogDirectionalLight")
 ##d = DirectionalLight("chernabogDirectionalLight")
 ##d.setDirection(Vec3(0, 1, 0))
 ##d.setDirection(Vec3(0, 1, 0))
 ##d.setColor(Vec4(1))
 ##d.setColor(Vec4(1))
 ###d.setColor(Vec4(0.9, 0.7, 0.7, 1.000))
 ###d.setColor(Vec4(0.9, 0.7, 0.7, 1.000))
-##dNP = s.attachNewNode(d.upcastToPandaNode())
+##dNP = s.attachNewNode(d)
 ##b.setLight(dNP)
 ##b.setLight(dNP)
 ##
 ##
 ##ival = Sequence(LerpPosInterval(bs.lightPath, 0.0, Vec3(-200, 0, 50)),
 ##ival = Sequence(LerpPosInterval(bs.lightPath, 0.0, Vec3(-200, 0, 50)),

+ 27 - 16
direct/src/showbase/ShowBase.py

@@ -47,10 +47,6 @@ if __debug__:
     from . import OnScreenDebug
     from . import OnScreenDebug
 from . import AppRunnerGlobal
 from . import AppRunnerGlobal
 
 
-def legacyRun():
-    assert builtins.base.notify.warning("run() is deprecated, use base.run() instead")
-    builtins.base.run()
-
 @atexit.register
 @atexit.register
 def exitfunc():
 def exitfunc():
     if getattr(builtins, 'base', None) is not None:
     if getattr(builtins, 'base', None) is not None:
@@ -369,7 +365,6 @@ class ShowBase(DirectObject.DirectObject):
         builtins.bboard = self.bboard
         builtins.bboard = self.bboard
         # Config needs to be defined before ShowBase is constructed
         # Config needs to be defined before ShowBase is constructed
         #builtins.config = self.config
         #builtins.config = self.config
-        builtins.run = legacyRun
         builtins.ostream = Notify.out()
         builtins.ostream = Notify.out()
         builtins.directNotify = directNotify
         builtins.directNotify = directNotify
         builtins.giveNotify = giveNotify
         builtins.giveNotify = giveNotify
@@ -389,6 +384,12 @@ class ShowBase(DirectObject.DirectObject):
             builtins.aspect2dp = self.aspect2dp
             builtins.aspect2dp = self.aspect2dp
             builtins.pixel2dp = self.pixel2dp
             builtins.pixel2dp = self.pixel2dp
 
 
+        # Now add this instance to the ShowBaseGlobal module scope.
+        from . import ShowBaseGlobal
+        builtins.run = ShowBaseGlobal.run
+        ShowBaseGlobal.base = self
+        ShowBaseGlobal.__dev__ = self.__dev__
+
         if self.__dev__:
         if self.__dev__:
             ShowBase.notify.debug('__dev__ == %s' % self.__dev__)
             ShowBase.notify.debug('__dev__ == %s' % self.__dev__)
         else:
         else:
@@ -396,10 +397,10 @@ class ShowBase(DirectObject.DirectObject):
 
 
         self.createBaseAudioManagers()
         self.createBaseAudioManagers()
 
 
-        if self.__dev__ or self.config.GetBool('want-e3-hacks', False):
-            if self.config.GetBool('track-gui-items', True):
-                # dict of guiId to gui item, for tracking down leaks
-                self.guiItems = {}
+        if self.__dev__ and self.config.GetBool('track-gui-items', False):
+            # dict of guiId to gui item, for tracking down leaks
+            if not hasattr(ShowBase, 'guiItems'):
+                ShowBase.guiItems = {}
 
 
         # optionally restore the default gui sounds from 1.7.2 and earlier
         # optionally restore the default gui sounds from 1.7.2 and earlier
         if ConfigVariableBool('orig-gui-sounds', False).getValue():
         if ConfigVariableBool('orig-gui-sounds', False).getValue():
@@ -511,9 +512,15 @@ class ShowBase(DirectObject.DirectObject):
 
 
         # Remove the built-in base reference
         # Remove the built-in base reference
         if getattr(builtins, 'base', None) is self:
         if getattr(builtins, 'base', None) is self:
+            del builtins.run
             del builtins.base
             del builtins.base
             del builtins.loader
             del builtins.loader
             del builtins.taskMgr
             del builtins.taskMgr
+            ShowBaseGlobal = sys.modules.get('direct.showbase.ShowBaseGlobal', None)
+            if ShowBaseGlobal:
+                del ShowBaseGlobal.base
+
+        self.aspect2d.node().removeAllChildren()
 
 
         # [gjeon] restore sticky key settings
         # [gjeon] restore sticky key settings
         if self.config.GetBool('disable-sticky-keys', 0):
         if self.config.GetBool('disable-sticky-keys', 0):
@@ -1097,13 +1104,18 @@ class ShowBase(DirectObject.DirectObject):
         self.render2d.setMaterialOff(1)
         self.render2d.setMaterialOff(1)
         self.render2d.setTwoSided(1)
         self.render2d.setTwoSided(1)
 
 
+        # We've already created aspect2d in ShowBaseGlobal, for the
+        # benefit of creating DirectGui elements before ShowBase.
+        from . import ShowBaseGlobal
+
         ## The normal 2-d DisplayRegion has an aspect ratio that
         ## The normal 2-d DisplayRegion has an aspect ratio that
         ## matches the window, but its coordinate system is square.
         ## matches the window, but its coordinate system is square.
         ## This means anything we parent to render2d gets stretched.
         ## This means anything we parent to render2d gets stretched.
         ## For things where that makes a difference, we set up
         ## For things where that makes a difference, we set up
         ## aspect2d, which scales things back to the right aspect
         ## aspect2d, which scales things back to the right aspect
         ## ratio along the X axis (Z is still from -1 to 1)
         ## ratio along the X axis (Z is still from -1 to 1)
-        self.aspect2d = self.render2d.attachNewNode(PGTop("aspect2d"))
+        self.aspect2d = ShowBaseGlobal.aspect2d
+        self.aspect2d.reparentTo(self.render2d)
 
 
         aspectRatio = self.getAspectRatio()
         aspectRatio = self.getAspectRatio()
         self.aspect2d.setScale(1.0 / aspectRatio, 1.0, 1.0)
         self.aspect2d.setScale(1.0 / aspectRatio, 1.0, 1.0)
@@ -1260,8 +1272,7 @@ class ShowBase(DirectObject.DirectObject):
         if win == None:
         if win == None:
             win = self.win
             win = self.win
 
 
-        if win != None and win.getSideBySideStereo() and \
-                win.hasSize() and win.getSbsLeftYSize() != 0:
+        if win != None and win.hasSize() and win.getSbsLeftYSize() != 0:
             aspectRatio = float(win.getSbsLeftXSize()) / float(win.getSbsLeftYSize())
             aspectRatio = float(win.getSbsLeftXSize()) / float(win.getSbsLeftYSize())
         else:
         else:
             if win == None or not hasattr(win, "getRequestedProperties"):
             if win == None or not hasattr(win, "getRequestedProperties"):
@@ -1542,8 +1553,7 @@ class ShowBase(DirectObject.DirectObject):
             # mouse activity.
             # mouse activity.
             mw = self.buttonThrowers[0].getParent()
             mw = self.buttonThrowers[0].getParent()
             mouseRecorder = MouseRecorder('mouse')
             mouseRecorder = MouseRecorder('mouse')
-            self.recorder.addRecorder(
-                'mouse', mouseRecorder.upcastToRecorderBase())
+            self.recorder.addRecorder('mouse', mouseRecorder)
             np = mw.getParent().attachNewNode(mouseRecorder)
             np = mw.getParent().attachNewNode(mouseRecorder)
             mw.reparentTo(np)
             mw.reparentTo(np)
 
 
@@ -2726,9 +2736,10 @@ class ShowBase(DirectObject.DirectObject):
             # changed and update the camera lenses and aspect2d parameters
             # changed and update the camera lenses and aspect2d parameters
             self.adjustWindowAspectRatio(self.getAspectRatio())
             self.adjustWindowAspectRatio(self.getAspectRatio())
 
 
-            if win.getSideBySideStereo() and win.hasSize() and win.getSbsLeftYSize() != 0:
+            if win.hasSize() and win.getSbsLeftYSize() != 0:
                 self.pixel2d.setScale(2.0 / win.getSbsLeftXSize(), 1.0, 2.0 / win.getSbsLeftYSize())
                 self.pixel2d.setScale(2.0 / win.getSbsLeftXSize(), 1.0, 2.0 / win.getSbsLeftYSize())
-                self.pixel2dp.setScale(2.0 / win.getSbsLeftXSize(), 1.0, 2.0 / win.getSbsLeftYSize())
+                if self.wantRender2dp:
+                    self.pixel2dp.setScale(2.0 / win.getSbsLeftXSize(), 1.0, 2.0 / win.getSbsLeftYSize())
             else:
             else:
                 xsize, ysize = self.getSize()
                 xsize, ysize = self.getSize()
                 if xsize > 0 and ysize > 0:
                 if xsize > 0 and ysize > 0:

+ 29 - 9
direct/src/showbase/ShowBaseGlobal.py

@@ -1,18 +1,39 @@
-"""instantiate global ShowBase object"""
+"""This module serves as a container to hold the global ShowBase instance, as
+an alternative to using the builtin scope.
+
+Note that you cannot directly import `base` from this module since ShowBase
+may not have been created yet; instead, ShowBase dynamically adds itself to
+this module's scope when instantiated."""
 
 
 __all__ = []
 __all__ = []
 
 
-from .ShowBase import *
+from .ShowBase import ShowBase, WindowControls
+from direct.directnotify.DirectNotifyGlobal import directNotify, giveNotify
+from panda3d.core import VirtualFileSystem, Notify, ClockObject, PandaSystem
+from panda3d.core import ConfigPageManager, ConfigVariableManager
+from panda3d.core import NodePath, PGTop
+from panda3d.direct import get_config_showbase
+
+config = get_config_showbase()
+__dev__ = config.GetBool('want-dev', __debug__)
 
 
-# Create the showbase instance
-# This should be created by the game specific "start" file
-#ShowBase()
-# Instead of creating a show base, assert that one has already been created
-assert base
+vfs = VirtualFileSystem.getGlobalPtr()
+ostream = Notify.out()
+globalClock = ClockObject.getGlobalClock()
+cpMgr = ConfigPageManager.getGlobalPtr()
+cvMgr = ConfigVariableManager.getGlobalPtr()
+pandaSystem = PandaSystem.getGlobalPtr()
+
+# This is defined here so GUI elements can be instantiated before ShowBase.
+aspect2d = NodePath(PGTop("aspect2d"))
 
 
 # Set direct notify categories now that we have config
 # Set direct notify categories now that we have config
 directNotify.setDconfigLevels()
 directNotify.setDconfigLevels()
 
 
+def run():
+    assert ShowBase.notify.warning("run() is deprecated, use base.run() instead")
+    base.run()
+
 def inspect(anObject):
 def inspect(anObject):
     # Don't use a regular import, to prevent ModuleFinder from picking
     # Don't use a regular import, to prevent ModuleFinder from picking
     # it up as a dependency when building a .p3d package.
     # it up as a dependency when building a .p3d package.
@@ -29,5 +50,4 @@ builtins.inspect = inspect
 
 
 # this also appears in AIBaseGlobal
 # this also appears in AIBaseGlobal
 if (not __debug__) and __dev__:
 if (not __debug__) and __dev__:
-    notify = directNotify.newCategory('ShowBaseGlobal')
-    notify.error("You must set 'want-dev' to false in non-debug mode.")
+    ShowBase.notify.error("You must set 'want-dev' to false in non-debug mode.")

+ 74 - 2
dmodels/src/gui/radio_button_gui.egg

@@ -1,6 +1,6 @@
 /*
 /*
  * This file was generated by:
  * This file was generated by:
- * interrogate -D EXPCL_DTOOLCONFIG= -nodb -python -promiscuous -I/home/rdb/panda3d-git/built/include -module panda3d.interrogatedb -library interrogatedb -string -true-names -do-module -oc pydtool.cxx ../../src/interrogatedb/interrogate_interface.h ../../src/interrogatedb/interrogate_request.h 
+ * interrogate -D EXPCL_DTOOLCONFIG= -nodb -python -promiscuous -I../../../built/include -module panda3d.interrogatedb -library interrogatedb -string -true-names -do-module -oc pydtool.cxx ../../src/interrogatedb/interrogate_interface.h ../../src/interrogatedb/interrogate_request.h
  *
  *
  */
  */
 
 
@@ -15,7 +15,7 @@
 #define PY_SSIZE_T_CLEAN 1
 #define PY_SSIZE_T_CLEAN 1
 
 
 #if PYTHON_FRAMEWORK
 #if PYTHON_FRAMEWORK
-  #include "Python/Python.h"
+  #include <Python/Python.h>
 #else
 #else
   #include "Python.h"
   #include "Python.h"
 #endif
 #endif
@@ -85,6 +85,9 @@ static PyObject *_inP07ytsqGH(PyObject *self, PyObject *args);
 static PyObject *_inP07yt7shV(PyObject *self, PyObject *args);
 static PyObject *_inP07yt7shV(PyObject *self, PyObject *args);
 static PyObject *_inP07ytA1eF(PyObject *self, PyObject *args);
 static PyObject *_inP07ytA1eF(PyObject *self, PyObject *args);
 static PyObject *_inP07yt776V(PyObject *self, PyObject *args);
 static PyObject *_inP07yt776V(PyObject *self, PyObject *args);
+static PyObject *_inP07ytryup(PyObject *self, PyObject *args);
+static PyObject *_inP07ytiytI(PyObject *self, PyObject *args);
+static PyObject *_inP07ytZc07(PyObject *self, PyObject *args);
 static PyObject *_inP07ytfaH0(PyObject *self, PyObject *args);
 static PyObject *_inP07ytfaH0(PyObject *self, PyObject *args);
 static PyObject *_inP07ytGB9D(PyObject *self, PyObject *args);
 static PyObject *_inP07ytGB9D(PyObject *self, PyObject *args);
 static PyObject *_inP07ytsxxs(PyObject *self, PyObject *args);
 static PyObject *_inP07ytsxxs(PyObject *self, PyObject *args);
@@ -94,6 +97,7 @@ static PyObject *_inP07yt4Px8(PyObject *self, PyObject *args);
 static PyObject *_inP07ytNHcs(PyObject *self, PyObject *args);
 static PyObject *_inP07ytNHcs(PyObject *self, PyObject *args);
 static PyObject *_inP07ytqHrb(PyObject *self, PyObject *args);
 static PyObject *_inP07ytqHrb(PyObject *self, PyObject *args);
 static PyObject *_inP07ytaOqq(PyObject *self, PyObject *args);
 static PyObject *_inP07ytaOqq(PyObject *self, PyObject *args);
+static PyObject *_inP07ytpTBb(PyObject *self, PyObject *args);
 static PyObject *_inP07ytqWOw(PyObject *self, PyObject *args);
 static PyObject *_inP07ytqWOw(PyObject *self, PyObject *args);
 static PyObject *_inP07ytHu7x(PyObject *self, PyObject *args);
 static PyObject *_inP07ytHu7x(PyObject *self, PyObject *args);
 static PyObject *_inP07ytwGnA(PyObject *self, PyObject *args);
 static PyObject *_inP07ytwGnA(PyObject *self, PyObject *args);
@@ -1237,6 +1241,56 @@ _inP07yt776V(PyObject *, PyObject *args) {
   return (PyObject *)NULL;
   return (PyObject *)NULL;
 }
 }
 
 
+/*
+ * Python simple wrapper for
+ * char const *interrogate_make_seq_scoped_name(MakeSeqIndex make_seq)
+ */
+static PyObject *
+_inP07ytryup(PyObject *, PyObject *args) {
+  int param0;
+  if (PyArg_ParseTuple(args, "i", &param0)) {
+    char const *return_value = interrogate_make_seq_scoped_name((MakeSeqIndex)param0);
+#if PY_MAJOR_VERSION >= 3
+    return PyUnicode_FromString(return_value);
+#else
+    return PyString_FromString(return_value);
+#endif
+  }
+  return (PyObject *)NULL;
+}
+
+/*
+ * Python simple wrapper for
+ * bool interrogate_make_seq_has_comment(ElementIndex element)
+ */
+static PyObject *
+_inP07ytiytI(PyObject *, PyObject *args) {
+  int param0;
+  if (PyArg_ParseTuple(args, "i", &param0)) {
+    bool return_value = interrogate_make_seq_has_comment((ElementIndex)param0);
+    return PyBool_FromLong(return_value);
+  }
+  return (PyObject *)NULL;
+}
+
+/*
+ * Python simple wrapper for
+ * char const *interrogate_make_seq_comment(ElementIndex element)
+ */
+static PyObject *
+_inP07ytZc07(PyObject *, PyObject *args) {
+  int param0;
+  if (PyArg_ParseTuple(args, "i", &param0)) {
+    char const *return_value = interrogate_make_seq_comment((ElementIndex)param0);
+#if PY_MAJOR_VERSION >= 3
+    return PyUnicode_FromString(return_value);
+#else
+    return PyString_FromString(return_value);
+#endif
+  }
+  return (PyObject *)NULL;
+}
+
 /*
 /*
  * Python simple wrapper for
  * Python simple wrapper for
  * char const *interrogate_make_seq_num_name(MakeSeqIndex make_seq)
  * char const *interrogate_make_seq_num_name(MakeSeqIndex make_seq)
@@ -1397,6 +1451,20 @@ _inP07ytaOqq(PyObject *, PyObject *args) {
   return (PyObject *)NULL;
   return (PyObject *)NULL;
 }
 }
 
 
+/*
+ * Python simple wrapper for
+ * bool interrogate_type_is_global(TypeIndex type)
+ */
+static PyObject *
+_inP07ytpTBb(PyObject *, PyObject *args) {
+  int param0;
+  if (PyArg_ParseTuple(args, "i", &param0)) {
+    bool return_value = interrogate_type_is_global((TypeIndex)param0);
+    return PyBool_FromLong(return_value);
+  }
+  return (PyObject *)NULL;
+}
+
 /*
 /*
  * Python simple wrapper for
  * Python simple wrapper for
  * char const *interrogate_type_name(TypeIndex type)
  * char const *interrogate_type_name(TypeIndex type)
@@ -2416,6 +2484,9 @@ static PyMethodDef python_simple_funcs[] = {
   { "interrogate_wrapper_unique_name", &_inP07yt7shV, METH_VARARGS },
   { "interrogate_wrapper_unique_name", &_inP07yt7shV, METH_VARARGS },
   { "interrogate_get_wrapper_by_unique_name", &_inP07ytA1eF, METH_VARARGS },
   { "interrogate_get_wrapper_by_unique_name", &_inP07ytA1eF, METH_VARARGS },
   { "interrogate_make_seq_seq_name", &_inP07yt776V, METH_VARARGS },
   { "interrogate_make_seq_seq_name", &_inP07yt776V, METH_VARARGS },
+  { "interrogate_make_seq_scoped_name", &_inP07ytryup, METH_VARARGS },
+  { "interrogate_make_seq_has_comment", &_inP07ytiytI, METH_VARARGS },
+  { "interrogate_make_seq_comment", &_inP07ytZc07, METH_VARARGS },
   { "interrogate_make_seq_num_name", &_inP07ytfaH0, METH_VARARGS },
   { "interrogate_make_seq_num_name", &_inP07ytfaH0, METH_VARARGS },
   { "interrogate_make_seq_element_name", &_inP07ytGB9D, METH_VARARGS },
   { "interrogate_make_seq_element_name", &_inP07ytGB9D, METH_VARARGS },
   { "interrogate_number_of_global_types", &_inP07ytsxxs, METH_VARARGS },
   { "interrogate_number_of_global_types", &_inP07ytsxxs, METH_VARARGS },
@@ -2425,6 +2496,7 @@ static PyMethodDef python_simple_funcs[] = {
   { "interrogate_get_type_by_name", &_inP07ytNHcs, METH_VARARGS },
   { "interrogate_get_type_by_name", &_inP07ytNHcs, METH_VARARGS },
   { "interrogate_get_type_by_scoped_name", &_inP07ytqHrb, METH_VARARGS },
   { "interrogate_get_type_by_scoped_name", &_inP07ytqHrb, METH_VARARGS },
   { "interrogate_get_type_by_true_name", &_inP07ytaOqq, METH_VARARGS },
   { "interrogate_get_type_by_true_name", &_inP07ytaOqq, METH_VARARGS },
+  { "interrogate_type_is_global", &_inP07ytpTBb, METH_VARARGS },
   { "interrogate_type_name", &_inP07ytqWOw, METH_VARARGS },
   { "interrogate_type_name", &_inP07ytqWOw, METH_VARARGS },
   { "interrogate_type_scoped_name", &_inP07ytHu7x, METH_VARARGS },
   { "interrogate_type_scoped_name", &_inP07ytHu7x, METH_VARARGS },
   { "interrogate_type_true_name", &_inP07ytwGnA, METH_VARARGS },
   { "interrogate_type_true_name", &_inP07ytwGnA, METH_VARARGS },

+ 0 - 33
dtool/src/attach/ctallihave

@@ -1,33 +0,0 @@
-#!/usr/bin/perl
-
-if ($#ARGV != -1) {
-   exit print "Usage: ctihave\n" ;
-}
-
-$tool = $ENV{"DTOOL"} ;
-if ( $tool eq "" ) {
-   die "not configured for using CTtools\n" ;
-}
-
-require "$tool/built/include/ctutils.pl" ;
-require "$tool/built/include/ctvspec.pl" ;
-require "$tool/built/include/ctquery.pl" ;
-require "$tool/built/include/ctproj.pl" ;
-require "$tool/built/include/ctcm.pl" ;
-
-$projs = $ENV{"CTPROJS"} ;
-@projsplit = split( / +/, $projs ) ;
-
-foreach $item ( @projsplit ) {
-    @items = split( /:/, $item ) ;
-    $thisproj = $items[0] ;
-    $thisflav = $items[1] ;
-    $thisspec = &CTResolveSpec( $thisproj, $thisflav ) ;
-    $result = $result . &CTCMIHave( $thisproj, $thisflav, $thisspec ) ;
-}
-if ( $result ne "" ) {
-    @splitlist = split( /\n/, $result ) ;
-    foreach $item ( @splitlist ) {
-	print $item . "\n" ;
-    }
-}

+ 0 - 150
dtool/src/attach/ctattach.drv

@@ -1,150 +0,0 @@
-#!/usr/bin/perl
-
-# acceptable forms:
-#   ctattach                     - give usage message
-#   ctattach project             - attach to the personal flavor of the project
-#   ctattach project flavor      - attach to a specific flavor of the project
-#   ctattach -                   - list projects that can be attached to
-#   ctattach project -           - list flavors of a given project
-#   ctattach - flavor            - list projects with a certain flavor
-#   ctattach -def project flavor - attach to project, setting CTDEFAULT_FLAV
-#                                  to flavor for the scope of this attach
-
-sub CTAttachUsage {
-   print STDERR "Usage: ctattach -def project flavor  -or-\n" ;
-   print STDERR "       ctattach project [flavor]     -or-\n" ;
-   print STDERR "       ctattach project -            -or-\n" ;
-   print STDERR "       ctattach - [flavor]\n" ;
-   &CTAttachWriteNullScript( $tmpname ) ;
-   print $tmpname . "\n" ;
-   exit;
-}
-
-$tool = $ENV{"DTOOL"} ;
-if ( $tool eq "" ) {
-    die "\$" . "DTOOL environment must be set to use CTtools\n" ;
-}
-
-require "$tool/built/include/ctattch.pl" ;
-
-$tmpname = "/tmp/script.$$" ;
-
-if ( $#ARGV == -1 ) {
-    &CTUDebug( "got no arguments\n" ) ;
-    &CTAttachUsage ;
-}
-
-$idx = 0 ;
-$proj = "" ;
-$flav = "" ;
-$noflav = 0 ;
-$defflav = "" ;
-$spread = 0 ;
-$anydef = 0 ;
-
-#
-# parse arguemnts
-#
-
-if ( $ARGV[$idx] eq "-def" ) {
-   &CTUDebug( "got '-def' parameter\n" ) ;
-   if ( $#ARGV < ($idx + 2) ) {
-       &CTUDebug( "not enough arguments after -def\n" ) ;
-       &CTAttachUsage ;
-   }
-   $defflav = $ARGV[$idx+2] ;
-   $spread = 1;
-   &CTUDebug( "spread default flavor is '$defflav'\n" ) ;
-   $idx++ ;
-} else {
-   if ( $ENV{"CTDEFAULT_FLAV"} ne "" ) {
-      $defflav = $ENV{"CTDEFAULT_FLAV"} ;
-      &CTUDebug( "environment default flavor is '$defflav'\n" ) ;
-   }
-}
-
-$proj = $ARGV[$idx] ;
-&CTUDebug( "project is '$proj'\n" ) ;
-
-if ( $defflav eq "" ) {
-   $defflav = "default" ;
-   &CTUDebug( "no environmental default, using 'default'\n" ) ;
-}
-
-if ( $#ARGV > $idx ) {
-   $flav = $ARGV[$idx+1] ;
-   &CTUDebug( "provided flavor is '$flav'\n" ) ;
-} else {
-   if ( $proj ne "-" ) {
-      $flav = $defflav;
-      &CTUDebug( "using environment default flavor '$flav'\n" ) ;
-      $noflav = 1 ;
-   }
-}
-
-if (( $noflav == 1 ) || ( $flav eq "default" )) {
-   $anydef = 1 ;
-}
-
-#
-# act on the arguments we got
-#
-
-require "$tool/built/include/ctquery.pl" ;
-require "$tool/built/include/ctvspec.pl" ;
-
-if (( $proj eq "-" ) || ( $flav eq "-" )) {
-   if ( $#ARGV == 0 ) {
-      # list projects that can be attached to
-      print STDERR "Projects that can be attached to:\n" ;
-      $_ = &CTListAllProjects ;
-      @projlist = split ;
-      foreach $item ( @projlist ) {
-	 print STDERR "   $item\n" ;
-      }
-   } elsif ( $proj eq "-" ) {
-      # list project that have a given flavor
-      print STDERR "Projects that have a '$flav' flavor:\n" ;
-      $_ = &CTListAllProjects ;
-      @projlist = split ;
-      foreach $item ( @projlist ) {
-	 $tmp = &CTResolveSpec( $item, $flav ) ;
-	 if ( $tmp ne "" ) {
-	    print STDERR "   $item\n" ;
-	 }
-      }
-   } else {
-      # list flavors of a given project
-      print STDERR "Flavors of project '$proj':\n" ;
-      $_ = &CTListAllFlavors( $proj ) ;
-      @flavlist = split ;
-      foreach $item ( @flavlist ) {
-	 print STDERR "   $item\n" ;
-      }
-   }
-   &CTAttachWriteNullScript( $tmpname ) ;
-   print $tmpname . "\n" ;
-} else {
-   # output a real attachment
-   $curflav = &CTQueryProj( $proj ) ;
-   if (( $curflav eq "" ) || ( $noflav == 0 )) {
-      $envsep{"PATH"} = ":" ;
-      $envsep{"LD_LIBRARY_PATH"} = ":" ;
-      $envsep{"DYLD_LIBRARY_PATH"} = ":" ;
-      $envsep{"PFPATH"} = ":" ;
-      $envsep{"SSPATH"} = ":" ;
-      $envsep{"STKPATH"} = ":" ;
-      $envsep{"DC_PATH"} = ":" ;
-      $spec = &CTAttachCompute( $proj, $flav, $anydef ) ;
-      if ( $spec eq "" ) {
-	 &CTAttachWriteNullScript( $tmpname ) ;
-	 print $tmpname . "\n" ;
-      } else {
-	 &CTAttachWriteScript( $tmpname ) ;
-	 print $tmpname . "\n" ;
-      }
-   } else {
-      &CTAttachWriteNullScript( $tmpname ) ;
-      print $tmpname . "\n" ;
-   }
-}

+ 0 - 86
dtool/src/attach/ctattachcc

@@ -1,86 +0,0 @@
-#!/usr/bin/perl
-
-if ( $#ARGV != 5 ) {
-   print STDERR "This is for internal use by attach ONLY\n" ;
-   exit ;
-}
-
-$root = $ARGV[0] ;
-$view = $ARGV[1] ;
-$branch = $ARGV[2] ;
-$label = $ARGV[3] ;
-$vobname = $ARGV[4] ;
-$proj = $ARGV[5] ;
-$tmpname = "/tmp/config.$$" ;
-
-$emitted = 0 ;
-
-$ctdebug = $ENV{"CTATTACH_DEBUG"} ;
-
-if ($ctdebug) {
-   print STDERR "Params:\n  0: '$root'\n  1: '$view'\n  2: '$branch'\n" ;
-   print STDERR "  3: '$label'\n  4: '$vobname'\n  5: '$proj'\n" ;
-   print STDERR "making branch and label types for view " . $view . "\n" ;
-   print STDERR "executing: /usr/atria/bin/cleartool mkbrtype -vob /vobs/$vobname -c \"Branch type for the $view view\" $branch 2> /dev/null > /dev/null\n" ;
-   print STDERR "executing: /usr/atria/bin/cleartool mklbtype -vob /vobs/$vobname -c \"Label type for the $view view\" $label 2> /dev/null > /dev/null\n" ;
-}
-system "/usr/atria/bin/cleartool mkbrtype -vob /vobs/$vobname -c \"Branch type for the $view view\" $branch 2> /dev/null > /dev/null\n" ;
-system "/usr/atria/bin/cleartool mklbtype -vob /vobs/$vobname -c \"Label type for the $view view\" $label 2> /dev/null > /dev/null\n" ;
-
-if ($ctdebug) {
-   print STDERR "creating/updating the config-spec for view " . $view . "\n" ;
-}
-open( CTINTERFACE, "/usr/atria/bin/cleartool catcs -tag $view |" ) ;
-open( TMPFILE, "> $tmpname" ) ;
-while ( <CTINTERFACE> ) {
-   if ( $_ =~ "CHECKEDOUT" ) {
-      if ($ctdebug) {
-	 print STDERR "case 1:\noutputting: '$_'\n" ;
-      }
-      print TMPFILE "$_" ;
-   } elsif (( $_ =~ /^element \*/ ) && ( $_ =~ "/main/LATEST" ) &&
-	    !( $_ =~ /\/$proj\// )) {
-      if ( ! $emitted ) {
-	 $emitted = 1 ;
-	 print TMPFILE "element $root/... .../$branch/LATEST\n" ;
-	 print TMPFILE "element $root/... $label -mkbranch $branch\n" ;
-	 print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ;
-	 if ($ctdebug) {
-	    print STDERR "case 2:\n" ;
-	    print STDERR "outputting: 'element $root/... .../$branch/LATEST'\n" ;
-	    print STDERR "outputting: 'element $root/... $label -mkbranch $branch'\n" ;
-	    print STDERR "outputting: 'element $root/... /main/LATEST -mkbranch $branch'\n" ;
-	 }
-      }
-      if ($ctdebug) {
-	 print STDERR "case 3:\n" ;
-	 print STDERR "outputting: '$_'\n" ;
-      }
-      print TMPFILE "$_" ;
-   } elsif ( $_ =~ /\/$proj\// ) {
-      if ( ! $emitted ) {
-	 $emitted = 1 ;
-	 print TMPFILE "element $root/... .../$branch/LATEST\n" ;
-	 print TMPFILE "element $root/... $label -mkbranch $branch\n" ;
-	 print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ;
-	 if ($ctdebug) {
-	    print STDERR "case 4:\n" ;
-	    print STDERR "outputting: 'element $root/... .../$branch/LATEST'\n" ;
-	    print STDERR "outputting: 'element $root/... $label -mkbranch $branch'\n" ;
-	    print STDERR "outputting: 'element $root/... /main/LATEST -mkbranch $branch'\n" ;
-	 }
-      }
-   } else {
-      if ($ctdebug) {
-	 print STDERR "case 5:\n" ;
-	 print STDERR "outputting: '$_'\n" ;
-      }
-      print TMPFILE "$_" ;
-   }
-}
-close( CTINTERFACE ) ;
-close( TMPFILE ) ;
-if ($ctdebug) {
-   print STDERR "output to execute: '/usr/atria/bin/cleartool setcs -tag $view $tmpname ; rm $tmpname'\n" ;
-}
-system "/usr/atria/bin/cleartool setcs -tag $view $tmpname ; rm $tmpname\n" ;

+ 0 - 530
dtool/src/attach/ctattch.pl

@@ -1,530 +0,0 @@
-require "$tool/built/include/ctutils.pl" ;
-
-$shell_type = "csh" ;
-if ( $ENV{"SHELL_TYPE"} ne "" ) {
-    if ( $ENV{"SHELL_TYPE"} eq "sh" ) {
-    $shell_type = "sh" ;
-    }
-}
-
-$docnt = 0 ;
-@attachqueue = () ;
-
-require "$tool/built/include/ctquery.pl" ;
-
-# force set a variable in the 'new' environment
-# input is in:
-# $_[0] = variable
-# $_[1] = value
-#
-# output is in:
-# %newenv = variable marked to be set to value
-sub CTAttachSet {
-    if ( ( $_[0] ne "" ) && ( $_[1] ne "" ) ) {
-    &CTUDebug( "setting " . $_[0] . " to '" . $_[1] . "'\n" ) ;
-    $newenv{$_[0]} = $_[1] ;
-    }
-}
-
-# get a variable from the environment and split it out to unified format
-# (ie: space separated)
-# input is in:
-# $_[0] = variable to get
-#
-# output is in:
-# string returned with value
-sub CTSpoolEnv {
-    local( $ret ) = $ENV{$_[0]} ;
-    if ( $envsep{$_[0]} ne "" ) {
-    local( @splitlist ) = split( $envsep{$_[0]}, $ret );
-    $ret = join( " ", @splitlist ) ;
-    }
-    $ret ;
-}
-
-# modify a possibly existing variable to have a value in the 'new' environment
-# input is in:
-# $_[0] = variable
-# $_[1] = value
-# $_[2] = root
-# $_[3] = project
-#
-# output is in:
-# %newenv = variable adjusted to have the new value
-sub CTAttachMod {
-    &CTUDebug( "in CTAttachMod\n" ) ;
-    if ( $_[0] eq "CTPROJS" ) {
-    # as part of the system, this one is special
-    &CTUDebug( "doing a mod on $CTPROJS\n" ) ;
-    if ( $newenv{$_[0]} eq "" ) {
-        $newenv{$_[0]} = $ENV{$_[0]} ;
-    }
-    local( $proj ) = $_[3] ;
-    $proj =~ tr/A-Z/a-z/ ;
-    local( $curflav ) = &CTQueryProj( $proj ) ;
-    if ( $curflav ne "" ) {
-        local( $tmp ) = $_[3] . ":" . $curflav ;
-        if ( $newenv{$_[0]} =~ /$tmp/ ) {
-        local( $hold ) = $newenv{$_[0]} ;
-        $hold =~ s/$tmp/$_[1]/ ;
-        &CTUDebug( "already attached to " . $_[3] . " changing '" .
-               $tmp . "' to '" . $_[1] . "' yielding '" . $hold .
-               "'\n" ) ;
-        $newenv{$_[0]} = $hold ;
-        } else {
-        &CTUDebug( "prepending '" . $_[1] . "' to CTPROJS\n" ) ;
-        $newenv{$_[0]} = $_[1] . " " . $newenv{$_[0]} ;
-        }
-    } else {
-        &CTUDebug( "writing '" . $_[1] . "' to CTPROJS\n" ) ;
-        if ( $newenv{$_[0]} eq "" ) {
-        $newenv{$_[0]} = $_[1] ;
-        } else {
-        $newenv{$_[0]} = $_[1] . " " . $newenv{$_[0]} ;
-        }
-    }
-    } elsif ( ( $_[0] ne "" ) && ( $_[1] ne "" ) ) {
-    local( $dosimple ) = 0 ;
-    if ( $newenv{$_[0]} eq "" ) {
-        # not in our 'new' environment yet, add it.
-        # may still be empty
-        $newenv{$_[0]} = &CTSpoolEnv( $_[0] ) ;
-    }
-    if ( ! ( $newenv{$_[0]} =~ /$_[1]/ )) {
-        &CTUDebug( "'" . $_[1] . "' exists in " . $_[0] .
-               " testing for simple modification\n" ) ;
-        # if it's in there already, we're done before we started.
-        if ( $_[1] =~ /^$_[2]/ ) {
-        &CTUDebug( "new value contains root '" . $_[2] .
-               "', may not be able to do simple edit\n" ) ;
-        # damn, might need to do an in-place edit
-        local( $curroot ) = $ENV{$_[3]} ;
-        &CTUDebug( "current root for '" . $_[3] . "' is '" .
-               $curroot . "'\n" ) ;
-        if ( $curroot eq "" ) {
-            &CTUDebug( "can do simple edit\n" ) ;
-            $dosimple = 1 ;
-        } else {
-            local( $test ) = $_[1] ;
-            $test =~ s/^$_[2]// ;
-            $test = $curroot . $test ;
-            if ( $newenv{$_[0]} =~ /$test/ ) {
-            # there it is.  in-place edit
-            local( $foo ) = $newenv{$_[0]} ;
-            $foo =~ s/$test/$_[1]/ ;
-            &CTUDebug( "doing in-place edit on " . $_[0] .
-                   " changing '" . $test . "' to '" .
-                   $_[1] . "' yielding '" . $foo . "'\n" ) ;
-            $newenv{$_[0]} = $foo ;
-            } else {
-            &CTUDebug( "'" . $test . "' did not appear in $_[0]." .
-                                   "  Simple edit\n" ) ;
-            $dosimple = 1 ;
-            }
-        }
-        } else {
-        &CTUDebug( "new value does not contain root '" . $_[2] .
-               "', can do simple edit\n" ) ;
-        # don't have to sweat in-place edits
-        $dosimple = 1 ;
-        }
-    }
-    if ( $dosimple ) {
-        if ( $newenv{$_[0]} eq "" ) {
-        &CTUDebug( "no pre-existing value in " . $_[0] .
-               " setting it to '" . $_[1] . "'\n" ) ;
-        $newenv{$_[0]} = $_[1] ;
-        } elsif ( $envpostpend{$_[0]} ) {
-        &CTUDebug( "post-pending '" . $_[1] . "' to " . $_[0] .
-               "\n" ) ;
-        $newenv{$_[0]} = $newenv{$_[0]} . " " . $_[1] ;
-        } elsif ( $envpostpendexceptions{$_[0]}{$_[1]} ) {
-        &CTUDebug( "post-pending (by exception) '" . $_[1] . "' to '" . $_[0] .
-               "'\n" ) ;
-        $newenv{$_[0]} = $newenv{$_[0]} . " " . $_[1] ;
-        } else {
-        &CTUDebug( "pre-pending '" . $_[1] . "' to " . $_[0] .
-               "\n" ) ;
-        $newenv{$_[0]} = $_[1] . " " . $newenv{$_[0]} ;
-        }
-    }
-    }
-}
-
-require "$tool/built/include/ctcm.pl" ;
-
-# given the project and flavor, build the lists of variables to set/modify
-# input is in:
-# $_[0] = project
-# $_[1] = flavor
-# $_[2] = is some kind of default?
-#
-# output is in:
-# return value is config line
-# %newenv      = an image of those parts of the environment we want to change
-# %envsep      = seperator
-# %envcmd      = set or setenv
-# %envdo       = direct commands to add to attach script
-# %envpostpend = flag that variable should be postpended
-sub CTAttachCompute {
-   &CTUDebug( "in CTAttachCompute\n" ) ;
-   local( $done ) = 0 ;
-   local( $flav ) = $_[1] ;
-   local( $prevflav ) = &CTQueryProj( $_[0] ) ;
-   local( $spec ) ;
-   local( $root ) ;
-   if ( $_[2] && ( $prevflav ne "" )) {
-      # want some form of default attachment, and are already attached.  Short
-      # circuit.
-      $done = 1 ;
-   }
-
-   #
-   # choose real flavor and find/validate root
-   #
-   while ( ! $done ) {
-      $spec = &CTResolveSpec( $_[0], $flav ) ;
-      #print STDERR  "spec line '" . $spec . "' flav: '" . $flav ."'\n";
-      &CTUDebug( "spec line = '$spec'\n" ) ;
-      if ( $spec ne "" ) {
-         $root = &CTComputeRoot( $_[0], $flav, $spec ) ;
-         &CTCMSetup( $_[0], $spec, $flav ) ;
-         if ( -e $root ) {
-            $done = 1 ;
-         }
-      } else {
-         print STDERR "could not resolve '" . $flav . "'\n" ;
-         $done = 1 ;
-      }
-      if (( ! $done ) && $_[2] ) {
-         if ( $flav eq "install" ) {
-            # oh my! are we ever in trouble
-            # want some sort of default, but couldn't get to what we wanted
-            print STDERR "ctattach to install failed\n" ;
-            $spec = "" ;
-            $done = 1 ;
-         } elsif ( $flav eq "release" ) {
-            $flav = "install" ;
-         } elsif ( $flav eq "ship" ) {
-            $flav = "release" ;
-         } else {
-            $flav = "install" ;
-         }
-      } elsif ( ! $done ) {
-         $spec = "" ;
-         print STDERR "resolved '" . $flav . "' but '" . $root .
-             "' does not exist\n" ;
-         $done = 1 ;
-      }
-   }
-
-   #
-   # start real work
-   #
-   if ( $spec ne "" ) {
-      local( $proj ) = $_[0] ;
-      $proj =~ tr/a-z/A-Z/ ;
-      local( $item ) ;
-
-      # we scan the .init file first because if there are needed sub-attaches
-      # they must happen before the rest of our work
-      local( $init ) = "$root/built/etc/$_[0].init" ;
-      local( %localmod );
-      local( %localset );
-      local( %localsep );
-      local( %localcmd );
-      local( %localdo );
-      local( $localdocnt ) = 0 ;
-      local( %localpost );
-      local( %localpostexceptions ) = () ;
-      if ( -e $init ) {
-      &CTUDebug( "scanning " . $_[0] . ".init\n" ) ;
-      local( @linesplit ) ;
-      local( $linetmp ) ;
-      local( $loop ) ;
-      local( $looptmp ) ;
-      local( *INITFILE ) ;
-      open( INITFILE, "< $init" ) ;
-      while ( <INITFILE> ) {
-          s/\n$// ;
-          @linesplit = split( /\#/ ) ;
-          $_ = $linesplit[0] ;
-          if ( $_ =~ /^MODABS/ ) {
-          @linesplit = split ;
-          $linetmp = $linesplit[1] ;
-          shift( @linesplit ) ;
-          shift( @linesplit ) ;
-          $linesplitjoin = join( " ", @linesplit ) ;
-          if ( $linesplit[0] eq "-" ) {
-              shift( @linesplit ) ;
-              $linesplitjoin = join( " ", @linesplit ) ;
-              $localpostexceptions{$linetmp}{$linesplitjoin} = 1 ;
-              &CTUDebug( "Creating post-pend exception for '" . 
-                        $linetmp . "':'" . $linesplitjoin . "'\n" ) ;
-          }
-          if ( $localmod{$linetmp} eq "" ) {
-              $localmod{$linetmp} = $linesplitjoin ;
-          } else {
-              $localmod{$linetmp} = $localmod{$linetmp} . " " .
-                  $linesplitjoin ;
-          }
-          } elsif ( $_ =~ /^MODREL/ ) {
-          @linesplit = split ;
-          $linetmp = $linesplit[1] ;
-          shift( @linesplit ) ;
-          shift( @linesplit ) ;
-          $postexception = 0 ;
-          foreach $loop ( @linesplit ) {
-              if ( $loop eq "-" ) {
-                  $postexception = 1 ;
-                  next ;
-              }
-              $looptmp = $root . "/" . &CTUShellEval($loop) ;
-              if ( $postexception ) {
-                  $localpostexceptions{$linetmp}{$looptmp} = 1 ;
-                  &CTUDebug( "Creating post-pend exception for '" . 
-                             $linetmp . "':'" . $looptmp . "'\n" ) ;
-              }
-              if ( -e $looptmp ) {
-              if ( $localmod{$linetmp} eq "" ) {
-                  $localmod{$linetmp} = $looptmp ;
-              } else {
-                  $localmod{$linetmp} = $localmod{$linetmp} . " " .
-                  $looptmp ;
-              }
-              }
-          }
-          } elsif ( $_ =~ /^SETABS/ ) {
-          @linesplit = split ;
-          $linetmp = $linesplit[1] ;
-          shift( @linesplit ) ;
-          shift( @linesplit ) ;
-          if ( $localset{$linetmp} eq "" ) {
-              $localset{$linetmp} = join( " ", @linesplit ) ;
-          } else {
-              $localset{$linetmp} = $localset{$linetmp} . " " .
-              join( " ", @linesplit ) ;
-          }
-          } elsif ( $_ =~ /^SETREL/ ) {
-          @linesplit = split ;
-          $linetmp = $linesplit[1] ;
-          shift( @linesplit ) ;
-          shift( @linesplit ) ;
-          foreach $loop ( @linesplit ) {
-              $looptmp = $root . "/" . &CTUShellEval($loop) ;
-              if ( -e $looptmp ) {
-              if ( $localset{$linetmp} eq "" ) {
-                  $localset{$linetmp} = $looptmp ;
-              } else {
-                  $localset{$linetmp} = $localset{$linetmp} . " " .
-                  $looptmp ;
-              }
-              }
-          }
-          } elsif ( $_ =~ /^SEP/ ) {
-          @linesplit = split ;
-          $localsep{$linesplit[1]} = $linesplit[2] ;
-          } elsif ( $_ =~ /^CMD/ ) {
-          @linesplit = split ;
-          $localcmd{$linesplit[1]} = $linesplit[2] ;
-          } elsif ( $_ =~ /^DOCSH/ ) {
-          if ( $shell_type ne "sh" ) {
-              @linesplit = split ;
-              shift( @linesplit ) ;
-              $localdo{$localdocnt} = join( " ", @linesplit ) ;
-              $localdocnt++ ;
-          }
-          } elsif ( $_ =~ /^DOSH/ ) {
-          if ( $shell_type eq "sh" ) {
-              @linesplit = split ;
-              shift( @linesplit ) ;
-              $localdo{$localdocnt} = join( " ", @linesplit ) ;
-              $localdocnt++ ;
-          }
-          } elsif ( $_ =~ /^DO/ ) {
-          @linesplit = split ;
-          shift( @linesplit ) ;
-          $localdo{$localdocnt} = join( " ", @linesplit ) ;
-          $localdocnt++ ;
-          } elsif ( $_ =~ /^POSTPEND/ ) {
-          @linesplit = split ;
-          $localpost{$linesplit[1]} = 1 ;
-          } elsif ( $_ =~ /^ATTACH/ ) {
-          @linesplit = split ;
-          shift( @linesplit ) ;
-          foreach $loop ( @linesplit ) {
-              push( @attachqueue, $loop ) ;
-          }
-          } elsif ( $_ ne "" ) {
-          print STDERR "Unknown .init directive '$_'\n" ;
-          }
-      }
-      close( INITFILE ) ;
-      }
-
-      # now handle sub-attaches
-      &CTUDebug( "performing sub-attaches\n" ) ;
-      while ( @attachqueue != () ) {
-      $item = shift( @attachqueue ) ;
-      &CTUDebug( "attaching to " . $item . "\n" ) ;
-      &CTAttachCompute( $item, $defflav, 1 ) ;
-      }
-
-      # now we will do our extentions, then apply the mods from the .init
-      # file, if any
-      &CTUDebug( "extending paths\n" ) ;
-      local( $type ) = &CTSpecType( $spec ) ;
-      if ( $type eq "vroot" ) {
-      &CTAttachMod( "PATH", "/usr/atria/bin", $root, $proj ) ;
-      }
-
-      # For now, we will not check whether the various /bin, /lib,
-      # /inc directories exist before adding them to the paths.  This
-      # helps when attaching to unitialized trees that do not have
-      # these directories yet (but will shortly).
-
-      # However, we *will* filter out any trees whose name ends in
-      # "MODELS".  These don't have subdirectories that we care about
-      # in the normal sense.
-      if ( ! ( $proj =~ /MODELS$/ ) ) {
-
-          $item = $root . "/built/bin" ;
-          #if ( -e $item ) {
-      &CTAttachMod( "PATH", $item, $root, $proj ) ;
-          #}
-
-          $item = $root . "/built/lib" ;
-          #if ( -e $item ) {
-          &CTAttachMod( "PATH", $item, $root, $proj ) ;
-      &CTAttachMod( "LD_LIBRARY_PATH", $item, $root, $proj ) ;
-      &CTAttachMod( "DYLD_LIBRARY_PATH", $item, $root, $proj ) ;
-          #}
-
-          $item = $root . "/built/include" ;
-          #if ( -e $item ) {
-      &CTAttachMod( "CT_INCLUDE_PATH", $item, $root, $proj ) ;
-          #}
-
-          $item = $root . "/built/etc" ;
-          #if ( -e $item ) {
-      &CTAttachMod( "ETC_PATH", $item, $root, $proj ) ;
-          #}
-      }
-
-      &CTAttachMod( "CTPROJS", $proj . ":" . $flav, $root, $proj ) ;
-      &CTAttachSet( $proj, $root ) ;
-
-      # run thru the stuff saved up from the .init file
-      foreach $item ( keys %localsep ) {
-      $envsep{$item} = $localsep{$item} ;
-      }
-      foreach $item ( keys %localpost ) {
-      $envpostpend{$item} = $localpost{$item} ;
-      }
-      %envpostpendexceptions = %localpostexceptions;
-      foreach $item ( keys %localmod ) {
-      local( @splitthis ) = split( / +/, $localmod{$item} ) ;
-      local( $thing ) ;
-      foreach $thing ( @splitthis ) {
-          &CTAttachMod( $item, $thing, $root, $proj ) ;
-      }
-      }
-      foreach $item ( keys %localset ) {
-      &CTAttachSet( $item, $localset{$item} ) ;
-      }
-      foreach $item ( keys %localcmd ) {
-      $envcmd{$item} = $localcmd{$item} ;
-      }
-      for ($item = 0; $item < $localdocnt; $item++) {
-      $envdo{$docnt} = $localdo{$item} ;
-      $docnt++ ;
-      }
-      %envpostpendexceptions = () ;
-   }
-
-   &CTUDebug( "out of CTAttachCompute\n" ) ;
-   $spec ;
-}
-
-# write a script to NOT change the environment
-# Input is:
-# $_[0] = filename
-sub CTAttachWriteNullScript {
-   &CTUDebug( "in CTAttachWriteNullScript\n" ) ;
-   local( *OUTFILE ) ;
-   open( OUTFILE, ">$_[0]" ) ;
-   print OUTFILE "#!/bin/" . $shell_type . " -f\n" ;
-   print OUTFILE "echo No attachment actions performed\n" ;
-   print OUTFILE "rm -f $_[0]\n" ;
-   close( OUTFILE ) ;
-   &CTUDebug( "out of CTAtachWriteNullScript\n" ) ;
-}
-
-# write a script to setup the environment
-# Input is:
-# $_[0] = filename
-sub CTAttachWriteScript {
-   &CTUDebug( "in CTAttachWriteScript\n" ) ;
-   local( *OUTFILE ) ;
-   open( OUTFILE, ">$_[0]" ) ;
-   print OUTFILE "#!/bin/" . $shell_type . " -f\n" ;
-   local( $item ) ;
-
-   foreach $item ( keys %newenv ) {
-       local( $sep ) = " " ;
-       if ( $envsep{$item} ne "" ) {
-       $sep = $envsep{$item} ;
-       }
-       local( @splitlist ) = split( / +/, $newenv{$item} ) ;
-       local( $outval ) = join( $sep, @splitlist ) ;
-
-       if ( $shell_type eq "sh" ) {
-       print OUTFILE "$item=\"" . $outval . "\"\n" ;
-       if ( $envcmd{$item} ne "set" ) {
-           print OUTFILE "export $item\n" ;
-       }
-       } else {
-       if ( $envcmd{$item} ne "" ) {
-           print OUTFILE $envcmd{$item} . " $item " ;
-           if ( $envcmd{$item} eq "set" ) {
-           print OUTFILE "= ( " ;
-           }
-           print OUTFILE $outval ;
-           if ( $envcmd{$item} eq "set" ) {
-           print OUTFILE ")" ;
-           }
-           print OUTFILE "\n" ;
-       } else {
-           print OUTFILE "setenv $item \"$outval\"\n" ;
-               if ( $ctdebug ) {
-                   print OUTFILE "echo setting " . $item . " to '" . $outval . "'\n" ;
-               }
-       }
-       }
-   }
-
-   #if ( $newenv{"CDPATH"} ne "" ) {
-   #    if ( $shell_type ne "sh" ) {
-   #    print OUTFILE "set cdpath = ( \$" . "CDPATH )\n" ;
-   #        if ( $ctdebug ) {
-   #            print OUTFILE "echo assigning cdpath\n" ;
-   #        }
-   #    }
-   #}
-   for ($item = 0; $item < $docnt; $item++) {
-      print OUTFILE $envdo{$item} . "\n" ;
-      if ( $ctdebug ) {
-        print OUTFILE "echo doing '" . $envdo{$item} . "'\n" ;
-      }
-   }
-   if (! $ctdebug) {
-      print OUTFILE "rm -f $_[0]\n" ;
-   } else {
-      print OUTFILE "echo end of script $_[0]\n" ;
-      print STDERR "no self-destruct script '" . $_[0] . "'\n" ;
-   }
-   close( OUTFILE ) ;
-   &CTUDebug( "out of CTAttachWriteScript\n" ) ;
-}
-
-1;

+ 0 - 954
dtool/src/attach/ctattch.pl.rnd

@@ -1,954 +0,0 @@
-require "$tool/built/include/ctutils.pl" ;
-
-# get list of all projects
-sub CTAttachListProj {
-   if ($ctdebug ne "") {
-      print STDERR "in CTAttachListProj\n" ;
-   }
-   local( $ret ) = "" ;
-   local( $done ) = 0 ;
-   local( *DIRFILES ) ;
-   open( DIRFILES, "(cd /var/etc ; /bin/ls -1 *.vspec ; echo blahblah) |" ) ;
-   while ( ! $done ) {
-      $_ = <DIRFILES> ;
-      s/\n$// ;
-      if ( $_ eq "blahblah" ) {
-	 $done = 1 ;
-      } else {
-	 s/.vspec$// ;
-	 $ret = $ret . " " . $_ ;
-      }
-   }
-   close( DIRFILES ) ;
-   if ($ctdebug ne "") {
-      print STDERR "out of CTAttachListProj\n" ;
-   }
-   $ret ;
-}
-
-# get list of flavors for a project
-# $_[0] = project
-sub CTAttachListFlav {
-   if ($ctdebug) {
-      print STDERR "in CTAttachListFlav\n" ;
-   }
-   local( $ret ) = "" ;
-   $vobname = $_[0] ;
-   if ( -e "/var/etc/$_[0].vspec" ) {
-      local( *SPECFILE ) ;
-      open( SPECFILE, "</var/etc/$_[0].vspec" ) ;
-      local( @partlist ) ;
-      while ( <SPECFILE> ) {
-	 if ( $_ =~ /^VOBNAME/ ) {
-	    @partlist = split( /=/ ) ;
-	    $vobname = $partlist[1] ;
-	    $vobname =~ s/\n$// ;
-	 } else {
-	    @partlist = split( /:/ ) ;
-	    $ret = $ret . " " . $partlist[0] ;
-	 }
-      }
-      close( SPECFILE ) ;
-   } else {
-      print STDERR "CTAttachListFlav: cannot locate '/var/etc/$_[0]'\n" ;
-   }
-   if ($ctdebug) {
-      print STDERR "out of CTAttachListFlav\n" ;
-   }
-   $ret ;
-}
-
-# get the flavor line for the given project
-# $_[0] = project
-# $_[1] = flavor
-sub CTAttachFindFlav {
-   if ($ctdebug) {
-      print STDERR "in CTAttachFindFlav\n" ;
-   }
-   local( $ret ) = "" ;
-   $vobname = $_[0] ;
-   if ( -e "/var/etc/$_[0].vspec" ) {
-      local( *SPECFILE ) ;
-      open( SPECFILE, "</var/etc/$_[0].vspec" ) ;
-      local( $done ) = 0 ;
-      local( @partlist ) ;
-      while (( $_ = <SPECFILE> ) && ! $done ) {
-	 s/\n$// ;
-	 if ( $_ =~ /^VOBNAME/ ) {
-	    @partlist = split( /=/ ) ;
-	    $vobname = $partlist[1] ;
-	 } else {
-	    @partlist = split( /:/ ) ;
-	    if ( $partlist[0] eq $_[1] ) {
-	       $done = 1 ;
-	       $ret = join( " ", @partlist );
-	    }
-	 }
-      }
-      close( SPECFILE ) ;
-   } else {
-      print STDERR "CTAttachFindFlav: cannot locate '/var/etc/$_[0]'\n" ;
-   }
-   if ($ctdebug) {
-      if ($ret ne "") {
-	 print STDERR "found flavor " . $_[1] . " of project " . $_[0] . "\n" ;
-      } else {
-	 print STDERR "did not find flavor " . $_[1] . " of project " . $_[0] . "\n" ;
-      }
-      print STDERR "out of CTAttachFindFlav\n" ;
-   }
-   $ret ;
-}
-
-# given the project and flavor, resolve the final config line
-# $_[0] = project
-# $_[1] = flavor
-sub CTAttachResolve {
-   if ($ctdebug) {
-      print STDERR "in CTAttachResolve\n" ;
-   }
-   local( $spec ) = &CTAttachFindFlav( $_[0], $_[1] ) ;
-   local( $ret ) = "" ;
-   if ( $spec ne "" ) {
-      local( @speclist ) ;
-      @speclist = split( / +/, $spec ) ;
-      if ( $speclist[1] eq "root" ) {
-	 $ret = join( " " , @speclist ) ;
-	 if ($ctdebug) {
-	    print STDERR "resolved to a 'root'\n" ;
-	 }
-      } elsif  ( $speclist[1] eq "vroot" ) {
-	 if ( $ENV{"HAVE_ATRIA"} ne "" ) {
-	    $ret = join( " " , @speclist ) ;
-	    if ($ctdebug) {
-	       print STDERR "resolved to a 'vroot'\n" ;
-	    }
-	 }
-      } elsif ( $speclist[1] eq "ref" ) {
-	 local( $tmp ) = &CTUShellEval( $speclist[2] ) ;
-	 if ($ctdebug) {
-	    print STDERR "resolved to a 'ref', recursing\n" ;
-	 }
-	 $ret = &CTAttachResolve( $_[0], $tmp ) ;
-      } else {
-	 print STDERR "CTAttachResolve: unknown flavor type '$speclist[1]'\n" ;
-      }
-   }
-   if ($ctdebug) {
-      print STDERR "out of CTAttachResolve\n" ;
-   }
-   $ret ;
-}
-
-# given the config line, determine the view name
-# $_[0] = config line
-sub CTAttachComputeView {
-   if ($ctdebug) {
-      print STDERR "in CTAttachComputeView\n" ;
-   }
-   local( $ret ) = "" ;
-   if ( $_[0] ne "" ) {
-      local( @speclist ) ;
-      @speclist = split( / +/, $_[0] ) ;
-      if ( $speclist[1] eq "vroot" ) {
-	 local( $vname ) = $speclist[0] ;
-	 shift( @speclist ) ;
-	 shift( @speclist ) ;
-	 local( $item ) ;
-	 local( @itemlist ) ;
-	 foreach $item ( @speclist ) {
-	    @itemlist = split( /=/, $item ) ;
-	    if ( $itemlist[0] eq "VN" ) {
-	       $vname = $itemlist[1] ;
-	    }
-	 }
-	 $ret = &CTUShellEval( $vname ) ;
-      }
-   }
-   if ($ctdebug) {
-      print STDERR "config line '" . $_[0] . "' yields view name '" . $ret . "'\n" ;
-      print STDERR "out of CTAttachComputeView\n" ;
-   }
-   $ret ;
-}
-
-# given the config line, determine the branch name
-# $_[0] = config line
-sub CTAttachComputeBranch {
-   if ($ctdebug) {
-      print STDERR "in CTAttachComputeBranch\n" ;
-   }
-   local( $ret ) = "" ;
-   if ( $_[0] ne "" ) {
-      local( @speclist ) ;
-      @speclist = split( / +/, $_[0] ) ;
-      if ( $speclist[1] eq "vroot" ) {
-	 local( $bname ) = &CTAttachComputeView( $_[0] ) ;
-	 shift( @speclist ) ;
-	 shift( @speclist ) ;
-	 local( $item ) ;
-	 local( @itemlist ) ;
-	 foreach $item ( @speclist ) {
-	    @itemlist = split( /=/, $item ) ;
-	    if ( $itemlist[0] eq "BN" ) {
-	       $bname = $itemlist[1] ;
-	    }
-	 }
-	 $ret = &CTUShellEval( $bname ) ;
-      }
-   }
-   if ($ctdebug) {
-      print STDERR "config line '" . $_[0] . "' yields branch name '" . $ret . "'\n" ;
-      print STDERR "out of CTAttachComputeBranch\n" ;
-   }
-   $ret ;
-}
-
-# given the config line, determine the label name
-# $_[0] = config line
-sub CTAttachComputeLabel {
-   if ($ctdebug) {
-      print STDERR "in CTAttachComputeLabel\n" ;
-   }
-   local( $ret ) = "" ;
-   if ( $_[0] ne "" ) {
-      local( @speclist ) ;
-      @speclist = split( / +/, $_[0] ) ;
-      if ( $speclist[1] eq "vroot" ) {
-	 local( $lname ) = &CTAttachComputeView( $_[0] ) ;
-	 shift( @speclist ) ;
-	 shift( @speclist ) ;
-	 local( $item ) ;
-	 local( @itemlist ) ;
-	 foreach $item ( @speclist ) {
-	    @itemlist = split( /=/, $item ) ;
-	    if ( $itemlist[0] eq "LB" ) {
-	       $lname = $itemlist[1] ;
-	    }
-	 }
-	 $ret = &CTUShellEval( $lname ) ;
-	 $ret =~ tr/a-z/A-Z/ ;
-      }
-   }
-   if ($ctdebug) {
-      print STDERR "config line '" . $_[0] . "' yields label name '" . $ret . "'\n" ;
-      print STDERR "out of CTAttachComputeLabel\n" ;
-   }
-   $ret ;
-}
-
-# given the project name and config line, determine the root of the project
-# $_[0] = project
-# $_[1] = config line
-sub CTAttachComputeRoot {
-   if ($ctdebug) {
-      print STDERR "in CTAttachComputeRoot\n" ;
-   }
-   local( $ret ) = "" ;
-   if ( $_[1] ne "" ) {
-      local( @speclist ) ;
-      @speclist = split( / +/, $_[1] ) ;
-      if ( $speclist[1] eq "root" ) {
-	 $ret = $speclist[2] ;
-      } elsif ( $speclist[1] eq "vroot" ) {
-	 $ret = &CTAttachComputeView( $_[1] ) ;
-	 $ret = "/view/$ret/vobs/$vobname" ;
-      } else {
-	 print STDERR "CTAttachComputeRoot: unknown flavor type '$speclist[1]'\n" ;
-      }
-   }
-   if ($ctdebug) {
-      print STDERR "out of CTAttachComputeRoot\n" ;
-   }
-   $ret ;
-}
-
-# given the project name and config line, determine the root of the project as
-# needed by the config spec.
-# $_[0] = project
-# $_[1] = config line
-sub CTAttachComputeElemRoot {
-   if ($ctdebug) {
-      print STDERR "in CTAttachComputeElemRoot\n" ;
-   }
-   local( $ret ) = "" ;
-   if ( $_[1] ne "" ) {
-      local( @speclist ) ;
-      @speclist = split( / +/, $_[1] ) ;
-      if ( $speclist[1] eq "root" ) {
-	 $ret = $speclist[2] ;
-      } elsif ( $speclist[1] eq "vroot" ) {
-	 $ret = &CTAttachComputeView( $_[1] ) ;
-	 $ret = "/vobs/$vobname" ;
-      } else {
-	 print STDERR "CTAttachComputeElemRoot: unknown flavor type '$speclist[1]'\n" ;
-      }
-   }
-   if ($ctdebug) {
-      print STDERR "out of CTAttachComputeElemRoot\n" ;
-   }
-   $ret ;
-}
-
-# do whatever setup is needed for ClearCase
-# input is in:
-# $_[0] = project
-# $_[1] = $spec
-sub CTAttachCCSetup {
-   if ($ctdebug) {
-      print STDERR "in CTAttachCCSetup\n" ;
-   }
-   local( $root ) = &CTAttachComputeElemRoot( $_[0], $_[1] ) ;
-   local( $view ) = &CTAttachComputeView( $_[1] ) ;
-   local( $branch ) = &CTAttachComputeBranch( $_[1] ) ;
-   local( $label ) = &CTAttachComputeLabel( $_[1] ) ;
-   local( *CTINTERFACE ) ;
-   local( *TMPFILE ) ;
-   local( $tmpname ) = "/tmp/config.$$" ;
-   local( $emitted ) = 0 ;
-
-   if ($ctdebug) {
-      print STDERR "checking for existance of view '" . $view . "'\n" ;
-   }
-   open( CTINTERFACE, "/usr/atria/bin/cleartool lsview $view |" ) ;
-   $_ = <CTINTERFACE> ;
-   close( CTINTERFACE ) ;
-   if ( $_ eq "" ) {             # need to make the view
-      if ($ctdebug) {
-	 print STDERR "creating view '" . $view . "'\n" ;
-      }
-      system "umask 2 ; /usr/atria/bin/cleartool mkview -tag $view /var/views/$view.vws 2> /dev/null > /dev/null ; /usr/atria/bin/cleartool startview $view 2> /dev/null > /dev/null\n" ;
-   } elsif ( ! ( $_ =~ /\*/ )) { # need to start the view
-      if ($ctdebug) {
-	 print STDERR "starting view '" . $view . "'\n" ;
-      }
-      system "/usr/atria/bin/cleartool startview $view 2> /dev/null > /dev/null &\n" ;
-   }
-
-   if ($ctdebug) {
-      print STDERR "making branch and label types for view " . $view . "\n" ;
-   }
-   system "/usr/atria/bin/cleartool mkbrtype -vob /vobs/$vobname -c \"Branch type for the $view view\" $branch 2> /dev/null > /dev/null &\n" ;
-   system "/usr/atria/bin/cleartool mklbtype -vob /vobs/$vobname -c \"Label type for the $view view\" $label 2> /dev/null > /dev/null &\n" ;
-
-   if ($ctdebug) {
-      print STDERR "creating/updating the config-spec for view " . $view . "\n" ;
-   }
-   open( CTINTERFACE, "/usr/atria/bin/cleartool catcs -tag $view |" ) ;
-   open( TMPFILE, "> $tmpname" ) ;
-   while ( <CTINTERFACE> ) {
-      if ( $_ =~ "CHECKEDOUT" ) {
-	 print TMPFILE "$_" ;
-      } elsif (( $_ =~ /^element \*/ ) && ( $_ =~ "/main/LATEST" ) &&
-	       !( $_ =~ /$_[0]/ )) {
-	 if ( ! $emitted ) {
-	    $emitted = 1 ;
-	    print TMPFILE "element $root/... .../$branch/LATEST\n" ;
-	    print TMPFILE "element $root/... $label -mkbranch $branch\n" ;
-	    print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ;
-	 }
-	 print TMPFILE "$_" ;
-      } elsif ( $_ =~ /$_[0]/ ) {
-	 if ( ! $emitted ) {
-	    $emitted = 1 ;
-	    print TMPFILE "element $root/... .../$branch/LATEST\n" ;
-	    print TMPFILE "element $root/... $label -mkbranch $branch\n" ;
-	    print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ;
-	 }
-      } else {
-	 print TMPFILE "$_" ;
-      }
-   }
-   close( CTINTERFACE ) ;
-   close( TMPFILE ) ;
-   system "/usr/atria/bin/cleartool setcs -tag $view $tmpname ; rm $tmpname &\n" ;
-   if ($ctdebug) {
-      print STDERR "out of CTAttachCCSetup\n" ;
-   }
-}
-
-# do whatever setup is needed for ClearCase, but do it in the background
-# input is in:
-# $_[0] = project
-# $_[1] = $spec
-sub CTAttachCCSetupBG {
-   if ($ctdebug) {
-      print STDERR "in CTAttachCCSetupBG\n" ;
-   }
-   local( $root ) = &CTAttachComputeElemRoot( $_[0], $_[1] ) ;
-   local( $view ) = &CTAttachComputeView( $_[1] ) ;
-   local( $branch ) = &CTAttachComputeBranch( $_[1] ) ;
-   local( $label ) = &CTAttachComputeLabel( $_[1] ) ;
-
-   system "$tool/bin/ctattachcc $root $view $branch $label $vobname $_[0]\n" ;
-
-   if ($ctdebug) {
-      print STDERR "out of CTAttachCCSetupBG\n" ;
-   }
-}
-
-# prepend an entry onto the envmod of the given key.
-# input is in:
-# $_[0] = key
-# $_[1] = data
-#
-# output is in:
-# %envmod = has 'data' prepended at 'key'
-sub CTAttachPrependMod {
-   if ( $envmod{$_[0]} eq "" ) {
-      $envmod{$_[0]} = $_[1] ;
-   } else {
-      $envmod{$_[0]} = $_[1] . " " . $envmod{$_[0]} ;
-   }
-}
-
-# postpend an entry onto the envmod of the given key.
-# input is in:
-# $_[0] = key
-# $_[1] = data
-#
-# output is in:
-# %envmod = has 'data' postpended at 'key'
-sub CTAttachPostpendMod {
-   if ( $envmod{$_[0]} eq "" ) {
-      $envmod{$_[0]} = $_[1] ;
-   } else {
-      $envmod{$_[0]} = $envmod{$_[0]} . " " . $_[1] ;
-   }
-}
-
-# pre/post-pend an entry onto the envmod of the given key, as set/controlled
-# by envpospend, et al.
-# input is in:
-# $_[0] = key
-# $_[1] = data
-#
-# output is in:
-# %envmod = data pre/post pended at the given key
-sub CTAttachAddToMod {
-   if ($envpostpend{$_[0]} ne "") {
-      &CTAttachPostpendMod( $_[0], $_[1] ) ;
-   } else {
-      &CTAttachPrependMod( $_[0], $_[1] ) ;
-   }
-}
-
-# prepend the given entry to the envset of the given key
-# input is in:
-# $_[0] = key
-# $_[1] = data
-#
-# output is in:
-# %envset = prepended at key with data
-sub CTAttachPrependSet {
-   local( $sep ) = " " ;
-   if ( $envsep{$_[0]} ne "" ) {
-      $sep = $envsep{$_[0]} ;
-   }
-   if ($envset{$_[0]} ne "") {
-      $envset{$_[0]} = $_[1] . $sep . $envset{$_[0]} ;
-   } else {
-      $envset{$_[0]} = $_[1] ;
-   }
-}
-
-# postpend the given entry to the envset of the given key
-# input is in:
-# $_[0] = key
-# $_[1] = data
-#
-# output is in:
-# %envset = postpended at key with data
-sub CTAttachPostpendSet {
-   local( $sep ) = " " ;
-   if ( $envsep{$_[0]} ne "" ) {
-      $sep = $envsep{$_[0]} ;
-   }
-   if ($envset{$_[0]} ne "") {
-      $envset{$_[0]} = $envset{$_[0]} . $sep . $_[1] ;
-   } else {
-      $envset{$_[0]} = $_[1] ;
-   }
-}
-
-# pre/post-pend an entry onto the envset of the given key, as set/controlled
-# by envpospend, et al.
-# input is in:
-# $_[0] = key
-# $_[1] = data
-#
-# output is in:
-# %envset = data pre/post pended at the given key
-sub CTAttachAddToSet {
-   if ($envpostpend{$_[0]} ne "") {
-      &CTAttachPostpendSet( $_[0], $_[1] ) ;
-   } else {
-      &CTAttachPrependSet( $_[0], $_[1] ) ;
-   }
-}
-
-$docnt = 0 ;
-@attachqueue = () ;
-
-require "$tool/built/include/ctquery.pl" ;
-
-# given the project and flavor, build the lists of variables to set/modify
-# input is in:
-# $_[0] = project
-# $_[1] = flavor
-# $_[2] = is some kind of default?
-#
-# output is in:
-# return value is config line
-# %envmod      = environment variables to modify
-# %envset      = environment variables to outright set
-# %envsep      = seperator
-# %envcmd      = set or setenv
-# %envdo       = direct commands to add to attach script
-# %envpostpend = flag that variable should be postpended
-sub CTAttachCompute {
-   if ($ctdebug) {
-      print STDERR "in CTAttachCompute\n" ;
-   }
-   local( $done ) = 0 ;
-   local( $flav ) = $_[1] ;
-   local( $prevflav ) = &CTQueryProj( $_[0] ) ;
-   local( $spec ) ;
-   local( $root ) ;
-   if ( $_[2] && ( $prevflav ne "" )) {
-      # short circuit attaching, we're already there.
-      $done = 1 ;
-   }
-   while ( ! $done ) {
-      $spec = &CTAttachResolve( $_[0], $flav ) ;
-      if ( $ctdebug ne "" ) {
-	 print STDERR "spec line = '$spec'\n" ;
-      }
-      if ( $spec ne "" ) {
-	 $root = &CTAttachComputeRoot( $_[0], $spec ) ;
-	 if ( -e $root ) {
-	    $done = 1 ;
-	    if ( $spec =~ /vroot/ ) {
-	       &CTAttachCCSetupBG( $_[0], $spec ) ;
-	    }
-	 } elsif ( $spec =~ /vroot/ ) {
-	    &CTAttachCCSetup( $_[0], $spec ) ;
-	    if ( -e $root ) {
-	       $done = 1 ;
-	    }
-	 }
-      }
-      if (( ! $done ) && $_[2] ) {
-	 if ( $flav eq "install" ) {
-	    # oh my! are we ever in trouble
-	    print STDERR "you are in a strange alien universe\n" ;
-	    $spec = "" ;
-	    $done = 1 ;
-	 } elsif ( $flav eq "release" ) {
-	    $flav = "install" ;
-	 } elsif ( $flav eq "ship" ) {
-	    $flav = "release" ;
-	 } else {
-	    $flav = "ship" ;
-	 }
-      }
-   }
-
-   if ( $spec ne "" ) {
-      local( $proj ) = $_[0] ;
-      $proj =~ tr/a-z/A-Z/ ;
-      local( $view ) = &CTAttachComputeView( $spec ) ;
-
-      if ($ctdebug) {
-	 print STDERR "extending paths\n" ;
-      }
-
-      &CTAttachAddToMod( "PATH", $root . "/bin" ) ;
-      &CTAttachAddToMod( "LD_LIBRARY_PATH", $root . "/lib" ) ;
-      &CTAttachAddToMod( "DYLD_LIBRARY_PATH", $root . "/lib" ) ;
-      #&CTAttachAddToMod( "CDPATH", $root . "/src/all" ) ;
-      &CTAttachAddToMod( "CT_INCLUDE_PATH", $root . "/include" ) ;
-      &CTAttachAddToMod( "DC_PATH", $root . "/etc" ) ;
-      &CTAttachAddToMod( "PFPATH", $root . "/etc/models" ) ;
-      &CTAttachAddToMod( "SSPATH", $root . "/lib/ss" ) ;
-      &CTAttachAddToMod( "STKPATH", $root . "/lib/stk" ) ;
-      &CTAttachAddToMod( "CTPROJS", $proj . ":" . $flav ) ;
-      $envset{$proj} = $root;
-
-#      if ( $view ne "" ) {
-#	 &CTAttachCCSetup( $_[0], $spec ) ;
-#      }
-
-      if ( -e "$root/etc/$_[0].init" ) {
-	 if ($ctdebug) {
-	    print STDERR "scanning .init file\n" ;
-	 }
-	 local( @linesplit ) ;
-	 local( $linetmp ) ;
-	 local( $loop );
-	 local( *INITFILE ) ;
-	 if ( -x "$root/etc/$_[0].init" ) {
-	    open( INITFILE, "$root/etc/$_[0].init $_[0] $_[1] $root |" ) ;
-	 } else {
-	    open( INITFILE, "< $root/etc/$_[0].init" ) ;
-	 }
-	 while ( <INITFILE> ) {
-	    s/\n$// ;
-	    if ( $_ =~ /^MODABS/ ) {
-	       @linesplit = split ;
-	       $linetmp = $linesplit[1] ;
-	       shift( @linesplit ) ;
-	       shift( @linesplit ) ;
-	       &CTAttachPostpendMod( $linetmp, &CTUShellEval(join(" ", @linesplit))) ;
-	    } elsif ( $_ =~ /^MODREL/ ) {
-	       @linesplit = split ;
-	       $linetmp = $linesplit[1] ;
-	       shift( @linesplit ) ;
-	       shift( @linesplit ) ;
-	       foreach $loop ( @linesplit ) {
-		  &CTAttachPostpendMod( $linetmp, $root . "/" . &CTUShellEval($loop)) ;
-	       }
-	    } elsif ( $_ =~ /^SETABS/ ) {
-	       @linesplit = split ;
-	       $linetmp = $linesplit[1] ;
-	       shift( @linesplit ) ;
-	       shift( @linesplit ) ;
-	       &CTAttachPrependSet( $linetmp, &CTUShellEval(join(" ", @linesplit))) ;
-	    } elsif ( $_ =~ /^SETREL/ ) {
-	       @linesplit = split ;
-	       $linetmp = $linesplit[1] ;
-	       shift( @linesplit ) ;
-	       shift( @linesplit ) ;
-	       foreach $loop ( @linesplit ) {
-		   &CTAttachPrependSet( $linetmp, $root . "/" . &CTUShellEval($loop)) ;
-	       }
-	    } elsif ( $_ =~ /^SEP/ ) {
-	       @linesplit = split ;
-	       $envsep{$linesplit[1]} = $linesplit[2] ;
-	    } elsif ( $_ =~ /^CMD/ ) {
-	       @linesplit = split ;
-	       $envcmd{$linesplit[1]} = $linesplit[2] ;
-	    } elsif ( $_ =~ /^DO/ ) {
-	       @linesplit = split ;
-	       shift( @linesplit ) ;
-	       $envdo{$docnt} = join( " ", @linesplit ) ;
-	       $docnt++ ;
-	    } elsif ( $_ =~ /^POSTPEND/ ) {
-	       @linesplit = split ;
-	       $envpospend{$linesplit[1]} = 1 ;
-	    } elsif ( $_ =~ /^ATTACH/ ) {
-	       @linesplit = split ;
-	       shift( @linesplit ) ;
-	       foreach $loop ( @linesplit ) {
-		  push( @attachqueue, $loop ) ;
-	       }
-	    } else {
-	       print STDERR "Unknown .init directive '$_'\n" ;
-	    }
-	 }
-	 close( INITFILE ) ;
-      }
-
-      # save mods away until after sub-attach
-      local( %locmod ) ;
-      local( $item ) ;
-
-      foreach $item ( keys %envmod ) {
-	 $locmod{$item} = $envmod{$item} ;
-	 delete $envmod{$item} ;
-      }
-
-      # do sub-attaches
-      while ( @attachqueue != () ) {
-	 $item = shift( @attachqueue ) ;
-	 &CTAttachCompute( $item, $defflav, 1 ) ;
-      }
-
-      # restore saved mods and merge them in with existing
-      foreach $item ( keys %locmod ) {
-	 $envmod{$item} = $locmod{$item} ;
-	 delete $locmod{$item} ;
-      }
-
-      &CTAttachCheckVars( $_[0], $spec ) ;
-   }
-   if ($ctdebug) {
-      print STDERR "out of CTAttachCompute\n" ;
-   }
-   $spec ;
-}
-
-# take a mod list and merge it into set.  Uniqueifying as we go.
-# input is in:
-# $_[0] = mod list
-# $_[1] = key
-#
-# output is:
-# %envset = now has the mod line merged in with it.
-sub CTAttachMergeToSet {
-   if ( $ctdebug ) {
-      print STDERR "trying to add '$_[0]' to '$envset{$_[1]}'\n" ;
-   }
-   local( @splitlist ) ;
-   local( $loop ) ;
-   local( $sep ) = " " ;
-   if ( $envsep{$_[1]} ne "" ) {
-      $sep = $envsep{$_[1]} ;
-   }
-   @splitlist = split( / /, $_[0] ) ;
-   foreach $loop ( @splitlist ) {
-       if ( ! (( $envset{$_[1]} eq $loop ) ||
-	       ( $envset{$_[1]} =~ /^$loop$sep/ ) ||
-	       ( $envset{$_[1]} =~ /$sep$loop$/ ) ||
-	       ( $envset{$_[1]} =~ /$sep$loop$sep/ ))) {
-	  &CTAttachPostpendSet( $_[1], $loop ) ;
-       }
-   }
-   if ( $ctdebug ) {
-      print STDERR "yielding '$envset{$_[1]}'\n" ;
-   }
-}
-
-# Perform cleanup operations on the variable that are going to be set/modified
-# eg:
-#    * check to see if we're already attached to the project, and alter sets
-#      based on that
-#    * move mods of pre-existing variables to sets w/ the changes included
-#    * move mods of non-existing variables to sets
-#
-# input:
-# $_[0] = project
-# $_[1] = config line
-sub CTAttachCheckVars {
-   if ($ctdebug) {
-      print STDERR "in CTAttachCheckVars\n" ;
-   }
-   local( $prevflav ) = &CTQueryProj( $_[0] ) ;
-   local( $proj ) = $_[0] ;
-   $proj =~ tr/a-z/A-Z/ ;
-   local( $atria ) = "/usr/atria/bin" ;
-   if ( $ENV{"HAVE_ATRIA"} ne "" ) {
-      if ( !( $ENV{"PATH"} =~ /$atria/ )) {
-	 $envmod{"PATH"} = "$atria " . $envmod{"PATH"} ;
-      }
-   }
-   if ( $prevflav ne "" ) {  # are already attached to the project
-      if ( $ctdebug ne "" ) {
-	 print STDERR "am already attached\n" ;
-      }
-      local( $prevspec ) = &CTAttachResolve( $_[0], $prevflav ) ;
-      local( $prevroot ) = &CTAttachComputeRoot( $_[0], $prevspec ) ;
-      local( $root ) = &CTAttachComputeRoot( $_[0], $_[1] ) ;
-      local( $loop ) ;
-      local( $item ) ;
-      local( @splitlist ) ;
-      local( $modsave ) ;
-      foreach $item ( keys %envmod ) {
-	 if ( $ENV{$item} ne "" ) {
-	    if ( $ctdebug ne "" ) {
-	       print STDERR "'$item' is already in the environment\n" ;
-	    }
-	    if ( $item eq "CTPROJS" ) {
-	       local( $prevmark ) = $proj . ":" . $prevflav ;
-	       local( $curmark ) = $envmod{$item} ;
-	       if ( $ctdebug ne "" ) {
-		  print STDERR "changing '$prevmark' to '$curmark' yielding " ;
-	       }
-	       if ( ! $gotenv{$item} ) {
-		  $envset{$item} = $ENV{$item} ;
-	       }
-	       $envset{$item} =~ s/$prevmark/$curmark/ ;
-	       if ( $ctdebug ne "" ) {
-		  print STDERR "'$envset{$item}'\n" ;
-	       }
-	       delete $envmod{$item} ;
-	    } else {
-	       local( $src ) ;
-	       if ( $gotenv{$item} ) {
-		  $src = $envset{$item} ;
-	       } else {
-		  $src = $ENV{$item} ;
-	       }
-	       if ( $envsep{$item} ne "" ) {
-		  @splitlist = split( $envsep{$item}, $src ) ;
-	       } else {
-		  @splitlist = split( / +/, $src ) ;
-	       }
-	       $modsave = $envmod{$item} ;
-	       delete $envmod{$item} ;
-	       foreach $loop ( @splitlist ) {
-		  $loop =~ s/$prevroot/$root/ ;
-		  &CTAttachPostpendMod( $item, $loop ) ;
-	       }
-	       if ( $ctdebug ne "" ) {
-		  print STDERR "env '$src' -> '$envmod{$item}'\n" ;
-	       }
-	       @splitlist = split( / +/, $modsave ) ;
-	       foreach $loop ( @splitlist ) {
-		  if ( ! (( $envmod{$item} eq $loop ) ||
-			  ( $envmod{$item} =~ /^$loop / ) ||
-			  ( $envmod{$item} =~ / $loop$/ ) ||
-			  ( $envmod{$item} =~ / $loop / ))) {
-		     &CTAttachAddToMod( $item, $loop ) ;
-		  }
-	       }
-	       if ( $ctdebug ne "" ) {
-		  print STDERR "env final = '$envmod{$item}'\n" ;
-	       }
-	    }
-	 }
-	 if ( $envmod{$item} ne "" ) {
-	    $envset{$item} = $envmod{$item} ;
-	    if ( $envsep{$item} ne "" ) {
-	       $envset{$item} =~ s/ /$envsep{$item}/g ;
-	    }
-	    # &CTAttachMergeToSet( $envmod{$item}, $item ) ;
-	    delete $envmod{$item} ;
-	    $gotenv{$item} = 1 ;
-	 }
-      }
-   } else { # not already attached.  mods -> sets
-      if ( $ctdebug ne "" ) {
-	 print STDERR "am not already attached\n" ;
-      }
-      local( $item ) ;
-      local( $loop ) ;
-      local( $modsave ) ;
-      local( @splitlist ) ;
-      foreach $item ( keys %envmod ) {
-	 if ( $ENV{$item} ne "" ) {
-	    local( $src ) ;
-	    if ( $gotenv{$item} ) {
-	       $src = $envset{$item} ;
-	    } else {
-	       $src = $ENV{$item} ;
-	    }
-	    if ( $envsep{$item} ne "" ) {
-	       @splitlist = split( $envsep{$item}, $src ) ; 
-	    } else {
-	       @splitlist = split( / +/, $src ) ; 
-	    }
-	    $modsave = $envmod{$item} ;
-	    delete $envmod{$item} ;
-	    foreach $loop ( @splitlist ) {
-	       &CTAttachPostpendMod( $item, $loop ) ;
-	    }
-	    if ( $ctdebug ne "" ) {
-	       print STDERR "env '$src' -> '$envmod{$item}'\n" ;
-	    }
-	    @splitlist = split( / +/, $modsave ) ;
-	    foreach $loop ( @splitlist ) {
-	       if ( ! (( $envmod{$item} eq $loop ) ||
-		       ( $envmod{$item} =~ /^$loop / ) ||
-		       ( $envmod{$item} =~ / $loop$/ ) ||
-		       ( $envmod{$item} =~ / $loop / ))) {
-		  &CTAttachAddToMod( $item, $loop ) ;
-	       }
-	    }
-	    if ( $ctdebug ne "" ) {
-	       print STDERR "env final = '$envmod{$item}'\n" ;
-	    }
-	 }
-	 $envset{$item} = $envmod{$item} ;
-	 if ( $envsep{$item} ne "" ) {
-	    $envset{$item} =~ s/ /$envsep{$item}/g ;
-	 }
-	 # &CTAttachMergeToSet( $envmod{$item}, $item ) ;
-	 delete $envmod{$item} ;
-	 $gotenv{$item} = 1 ;
-      }
-   }
-   if ($ctdebug) {
-      print STDERR "out of CTAttachCheckVars\n" ;
-   }
-}
-
-# write a script to NOT change the environment
-# Input is:
-# $_[0] = filename
-sub CTAttachWriteNullScript {
-   if ($ctdebug) {
-      print STDERR "in CTAttachWriteNullScript\n" ;
-   }
-   local( *OUTFILE ) ;
-   open( OUTFILE, ">$_[0]" ) ;
-   print OUTFILE "#!/bin/csh -f\n" ;
-   print OUTFILE "echo No attachment actions performed\n" ;
-   print OUTFILE "/sbin/rm $_[0]\n" ;
-   close( OUTFILE ) ;
-   if ($ctdebug) {
-      print STDERR "out of CTAtachWriteNullScript\n" ;
-   }
-}
-
-# write a script to setup the environment
-# Input is:
-# $_[0] = filename
-sub CTAttachWriteScript {
-   if ($ctdebug) {
-      print STDERR "in CTAttachWriteScript\n" ;
-   }
-   local( *OUTFILE ) ;
-   open( OUTFILE, ">$_[0]" ) ;
-   print OUTFILE "#!/bin/csh -f\n" ;
-   local( $item ) ;
-   foreach $item ( keys %envset ) {
-      if ( $envcmd{$item} ne "" ) {
-	 print OUTFILE $envcmd{$item} . " $item " ;
-	 if ( $envcmd{$item} eq "set" ) {
-	    print OUTFILE "= " ;
-	 }
-	 print OUTFILE $envset{$item} . "\n" ;
-      } else {
-	 print OUTFILE "setenv $item \"$envset{$item}\"\n" ;
-      }
-   }
-   foreach $item ( keys %envmod ) {
-      print STDERR "SHOULD NOT BE HERE\n" ;
-      if ( $envcmd{$item} ne "" ) {
-	 print OUTFILE $envcmd{$item} . " $item " ;
-	 if ( $envcmd{$item} eq "set" ) {
-	    print OUTFILE "= ( " ;
-	 } else {
-	    print OUTFILE "\"" ;
-	 }
-      } else {
-	 print OUTFILE "setenv $item \"" ;
-      }
-      if ( $envsep{$item} ne "" ) {
-	 @itemlist = split( / +/, $envmod{$item} ) ;
-	 foreach $tmp ( @itemlist ) {
-	    print OUTFILE $tmp . $envsep{$item} ;
-	 }
-      } else {
-	 print OUTFILE $envmod{$item} ;
-      }
-      if ( $envcmd{$item} ne "" ) {
-	 if ( $envcmd{$item} eq "set" ) {
-	    print OUTFILE ")" ;
-	 } else {
-	    print OUTFILE "\"" ;
-	 }
-	 print OUTFILE "\n" ;
-      } else {
-	 print OUTFILE $ENV{$item} . "\"\n" ;
-      }
-   }
-   #if (( $envset{"CDPATH"} ne "" ) || ( $envmod{"CDPATH"} ne "" )) {
-   #   print OUTFILE "set cdpath = ( \$" . "CDPATH )\n" ;
-   #}
-   foreach $item ( keys %envdo ) {
-      print OUTFILE $envdo{$item} . "\n" ;
-   }
-   if (! $ctdebug) {
-      print OUTFILE "/sbin/rm $_[0]\n" ;
-   } else {
-      print STDERR "no self-destruct script '" . $_[0] . "'\n" ;
-   }
-   close( OUTFILE ) ;
-   if ($ctdebug) {
-      print STDERR "out of CTAttachWriteScript\n" ;
-   }
-}
-
-1;

+ 0 - 430
dtool/src/attach/ctccase.pl

@@ -1,430 +0,0 @@
-# given the config line, determine the view name
-# $_[0] = config line
-# $_[1] = flavor
-# $_[2] = project
-sub CTAttachComputeView {
-   &CTUDebug( "in CTAttachComputeView\n" ) ;
-   local( $ret ) = &CTResolveSpecName( $_[2], $_[1] ) ;
-   local( $options ) = &CTSpecOptions( $_[0] ) ;
-   if ( $options ne "" ) {
-       local( $name ) = &CTSpecFindOption( $options, "name" ) ;
-       if ( $name ne "" ) {
-	   &CTUDebug( "found a name '" . $name . "'\n" ) ;
-	   $ret = $name ;
-       } else {
-	   &CTUDebug( "no name option found, going with default\n" ) ;
-       }
-   }
-   &CTUDebug( "config line '" . $_[0] . "' yields view name '" . $ret .
-	      "'\n" . "out of CTAttachComputeView\n" ) ;
-   $ret ;
-}
-
-# given the config line, determine the branch name
-# $_[0] = config line
-# $_[1] = flavor
-# $_[2] = project
-sub CTAttachComputeBranch {
-   &CTUDebug( "in CTAttachComputeBranch\n" ) ;
-   local( $ret ) = &CTAttachComputeView( $_[0], $_[1], $_[2] ) ;
-   &CTUDebug( "config line '" . $_[0] . "' yields branch name '" . $ret .
-	      "'\n" . "out of CTAttachComputeBranch\n" ) ;
-   $ret ;
-}
-
-# given the config line, determine the label name
-# $_[0] = config line
-# $_[1] = flavor
-# $_[2] = project
-sub CTAttachComputeLabel {
-   &CTUDebug( "in CTAttachComputeLabel\n" ) ;
-   local( $ret ) = &CTAttachComputeView( $_[0], $_[1], $_[2] ) ;
-   $ret =~ tr/a-z/A-Z/ ;
-   &CTUDebug( "config line '" . $_[0] . "' yields label name '" . $ret .
-	      "'\n" . "out of CTAttachComputeLabel\n" ) ;
-   $ret ;
-}
-
-# given the project name and config line, determine the root of the project as
-# needed by the config spec.
-# $_[0] = project
-# $_[1] = config line
-# $_[2] = flavor
-sub CTAttachComputeElemRoot {
-   &CTUDebug( "in CTAttachComputeElemRoot\n" ) ;
-   local( $ret ) = "/vobs/$_[0]" ;
-   &CTUDebug( "out of CTAttachComputeElemRoot\n" ) ;
-   $ret ;
-}
-
-# do whatever setup is needed for ClearCase
-# input is in:
-# $_[0] = project
-# $_[1] = $spec
-# $_[2] = flavor
-sub CTAttachCCSetup {
-   &CTUDebug( "in CTAttachCCSetup\n" ) ;
-   local( $root ) = &CTAttachComputeElemRoot( $_[0], $_[1], $_[2] ) ;
-   local( $view ) = &CTAttachComputeView( $_[1], $_[2], $_[0] ) ;
-   local( $branch ) = &CTAttachComputeBranch( $_[1], $_[2], $_[0] ) ;
-   local( $label ) = &CTAttachComputeLabel( $_[1], $_[2], $_[0] ) ;
-   local( *CTINTERFACE ) ;
-   local( *TMPFILE ) ;
-   local( $tmpname ) = "/tmp/config.$$" ;
-   local( $emitted ) = 0 ;
-
-   &CTUDebug( "checking for existance of view '" . $view . "'\n" ) ;
-   open( CTINTERFACE, "/usr/atria/bin/cleartool lsview $view |" ) ;
-   $_ = <CTINTERFACE> ;
-   close( CTINTERFACE ) ;
-   if ( $_ eq "" ) {             # need to make the view
-      &CTUDebug( "creating view '" . $view . "'\n" ) ;
-      system "umask 2 ; /usr/atria/bin/cleartool mkview -tag $view /var/views/$view.vws 2> /dev/null > /dev/null ; /usr/atria/bin/cleartool startview $view 2> /dev/null > /dev/null\n" ;
-   } elsif ( ! ( $_ =~ /\*/ )) { # need to start the view
-      &CTUDebug( "starting view '" . $view . "'\n" ) ;
-      system "/usr/atria/bin/cleartool startview $view 2> /dev/null > /dev/null &\n" ;
-   }
-
-   &CTUDebug( "making branch and label types for view " . $view . "\n" ) ;
-   system "/usr/atria/bin/cleartool mkbrtype -vob /vobs/$vobname -c \"Branch type for the $view view\" $branch 2> /dev/null > /dev/null &\n" ;
-   system "/usr/atria/bin/cleartool mklbtype -vob /vobs/$vobname -c \"Label type for the $view view\" $label 2> /dev/null > /dev/null &\n" ;
-
-   &CTUDebug( "creating/updating the config-spec for view " . $view . "\n" ) ;
-   open( CTINTERFACE, "/usr/atria/bin/cleartool catcs -tag $view |" ) ;
-   open( TMPFILE, "> $tmpname" ) ;
-   while ( <CTINTERFACE> ) {
-      if ( $_ =~ "CHECKEDOUT" ) {
-	 print TMPFILE "$_" ;
-      } elsif (( $_ =~ /^element \*/ ) && ( $_ =~ "/main/LATEST" ) &&
-	       !( $_ =~ /$_[0]/ )) {
-	 if ( ! $emitted ) {
-	    $emitted = 1 ;
-	    print TMPFILE "element $root/... .../$branch/LATEST\n" ;
-	    print TMPFILE "element $root/... $label -mkbranch $branch\n" ;
-	    print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ;
-	 }
-	 print TMPFILE "$_" ;
-      } elsif ( $_ =~ /$_[0]/ ) {
-	 if ( ! $emitted ) {
-	    $emitted = 1 ;
-	    print TMPFILE "element $root/... .../$branch/LATEST\n" ;
-	    print TMPFILE "element $root/... $label -mkbranch $branch\n" ;
-	    print TMPFILE "element $root/... /main/LATEST -mkbranch $branch\n" ;
-	 }
-      } else {
-	 print TMPFILE "$_" ;
-      }
-   }
-   close( CTINTERFACE ) ;
-   close( TMPFILE ) ;
-   system "/usr/atria/bin/cleartool setcs -tag $view $tmpname ; rm -f $tmpname &\n" ;
-   &CTUDebug( "out of CTAttachCCSetup\n" ) ;
-}
-
-# do whatever setup is needed for ClearCase, but do it in the background
-# input is in:
-# $_[0] = project
-# $_[1] = $spec
-# $_[2] = flavor
-sub CTAttachCCSetupBG {
-   &CTUDebug( "in CTAttachCCSetupBG\n" ) ;
-   local( $root ) = &CTAttachComputeElemRoot( $_[0], $_[1], $_[2] ) ;
-   local( $view ) = &CTAttachComputeView( $_[1], $_[2], $_[0] ) ;
-   local( $branch ) = &CTAttachComputeBranch( $_[1], $_[2], $_[0] ) ;
-   local( $label ) = &CTAttachComputeLabel( $_[1], $_[2], $_[0] ) ;
-
-   system "$tool/bin/ctattachcc $root $view $branch $label $vobname $_[0]\n" ;
-
-   &CTUDebug( "out of CTAttachCCSetupBG\n" ) ;
-}
-
-# given a possibly empty string, format it into a comment or -nc
-# input is in:
-# $_[0] = possible comment string
-#
-# output is:
-# string for use by ClearCase functions
-sub CTCcaseFormatComment {
-    local( $ret ) = "" ;
-    if ( $_[0] eq "" ) {
-	$ret = "-nc" ;
-    } else {
-        $ret = "-c \"" . $_[0] . "\"" ;
-    }
-    $ret ;
-}
-
-# make a versioned directory
-# input is in:
-# $_[0] = directory to create
-# $_[1] = curr dir
-# $_[2] = possible comment
-#
-# output:
-# return success or failure
-sub CTCcaseMkdir {
-    &CTUDebug( "in CTCcaseMkdir\n" ) ;
-    local( $ret ) = 0 ;
-    local( $dir ) = $_[0] ;
-    if ( ! ( $dir =~ /^\// )) {
-	$dir = $_[1] . "/" . $dir ;
-    }
-    local( $comment) = &CTCcaseFormatComment( $_[2] ) ;
-    # first we have to check out the parent directory
-    local( @alist ) = split( /\//, $dir ) ;
-    pop( @alist ) ;
-    local( $parent ) = join( "/", @alist ) ;
-    &CTUDebug( "parent directory of '" . $dir . "' is '" . $parent . "'\n" ) ;
-    $ret = system( "cleartool co -nc $parent\n" ) ;
-    if ( $ret == 0 ) {
-	# now make the dir
-	$ret = &CTURetCode( system( "cleartool mkdir " . $comment .
-				    " $dir\n" )) ;
-    } else {
-	$ret = 0 ;
-    }
-    &CTUDebug( "out of CTCcaseMkdir\n" ) ;
-    $ret ;
-}
-
-# make a versioned element
-# input is in:
-# $_[0] = element to version
-# $_[1] = curr dir
-# $_[2] = possible comment
-# $_[3] = possible eltype
-#
-# output:
-# return success or failure
-sub CTCcaseMkelem {
-    &CTUDebug( "in CTCcaseMkelem\n" ) ;
-    local( $ret ) = 0 ;
-    local( $elem ) = $_[0] ;
-    if ( ! ( $elem =~ /^\// )) {
-	$elem = $_[1] . "/" . $elem ;
-    }
-    local( $comment) = &CTCcaseFormatComment( $_[2] ) ;
-    local( $eltype ) = $_[3] ;
-    if ( $eltype ne "" ) {
-	$eltype = "-eltype " . $eltype ;
-    }
-    # first we have to check out the parent directory
-    local( @alist ) = split( /\//, $elem ) ;
-    pop( @alist ) ;
-    local( $parent ) = join( "/", @alist ) ;
-    &CTUDebug( "parent directory of '" . $elem . "' is '" . $parent . "'\n" ) ;
-    $ret = system( "cleartool co -nc $parent\n" ) ;
-    if ( $ret != 0 ) {
-	&CTUDebug( "checking out the dirctory gave return code: " . $ret .
-		  "\n" ) ;
-	$ret = 0 ;
-    }
-    # now make the elem
-    $ret = &CTURetCode( system( "cleartool mkelem " . $comment . " " .
-			       $eltype . " $elem\n" )) ;
-    &CTUDebug( "out of CTCcaseMkelem\n" ) ;
-    $ret ;
-}
-
-# done here so there will be coherence if multiple deltas are done
-require "ctime.pl" ;
-$timestamp = &ctime(time) ;
-$timestamp =~ s/\n$// ;
-@timelist = split( /\s+/, $timestamp ) ;
-$timestamp = $timelist[2] . $timelist[1] . $timelist[5] . "_" . $timelist[3] ;
-$timestamp =~ s/:/_/g ;
-
-# delta an element
-# input is in:
-# $_[0] = element to delta
-#
-# output:
-# return success or failure
-sub CTCcaseDelta {
-    require "$tool/built/include/ctdelta.pl" ;
-
-    &CTUDebug( "in CTCcaseDelta\n" ) ;
-    local( $ret ) = 0 ;
-    # this is ripped from the old ctdelta script
-    &CTDeltaCheckin( $_[0] ) ;
-    local( $ver ) = &CTDeltaGetVersion( $_[0] ) ;
-    &CTUDebug( "got version '" . $ver . "'\n" ) ;
-    if ( &CTDeltaOk( $ver )) {
-	local( @verlist ) = split( /\//, $ver ) ;
-	pop( @verlist ) ;
-	pop( @verlist ) ;
-	local( $ver2 ) = join( "/", @verlist ) ;
-	&CTUDebug( "ver2 = '" . $ver2 . "'\n" ) ;
-	&CTDeltaSafeMerge( $_[0], $ver, $ver2 ) ;
-	system "cleartool checkin -nc $_[0] 2> /dev/null > /dev/null" ;
-	&CTUDebug( "merge complete, doing branch check\n" ) ;
-	&CTDeltaBranchCheck( $_[0], $ver, $timestamp ) ;
-	&CTUDebug( "logging potentially felonious activity for future" .
-		   "  incrimination\n" ) ;
-	&CTDeltaLog( $_[0], $ver, $ver2 ) ;
-	# better detection needs to be done
-	$ret = 1 ;
-    } else {
-	&CTUDebug( "cannot merge '" . $_[0] . "', no branches.\n" ) ;
-    }
-    &CTUDebug( "out of CTCcaseDelta\n" ) ;
-    $ret ;
-}
-
-# checkout an element
-# input is in:
-# $_[0] = element to checkout
-# $_[1] = possible comment
-#
-# output:
-# return success or failure
-sub CTCcaseCheckout {
-    &CTUDebug( "in CTCcaseCheckout\n" ) ;
-    local( $comment) = &CTCcaseFormatComment( $_[1] ) ;
-    local( $ret ) = &CTURetCode( system( "cleartool co " . $comment .
-					 " $_[0]\n" )) ;
-    &CTUDebug( "out of CTCcaseCheckout\n" ) ;
-    $ret ;
-}
-
-# checkin an element
-# input is in:
-# $_[0] = element to checkin
-
-#
-# output:
-# return success or failure
-sub CTCcaseCheckin {
-    &CTUDebug( "in CTCcaseCheckin\n" ) ;
-    local( $comment) = &CTCcaseFormatComment( $_[1] ) ;
-    local( $ret ) = &CTURetCode( system( "cleartool ci " . $comment .
-					 " $_[0]\n" )) ;
-    &CTUDebug( "out of CTCcaseCheckin\n" ) ;
-    $ret ;
-}
-
-# uncheckout an element
-# input is in:
-# $_[0] = element to uncheckout
-#
-# output:
-# return success or failure
-sub CTCcaseUncheckout {
-    require "$tool/built/include/unco.pl" ;
-    &CTUDebug( "in CTCcaseUncheckout\n" ) ;
-    local( $ret ) = 1 ;
-    # need better error checking on this
-    system( "cleartool unco -rm $_[0]\n" ) ;
-    &CTUncoDoIt( $_[0] ) ;
-    &CTUDebug( "out of CTCcaseUncheckout\n" ) ;
-    $ret ;
-}
-
-# figure out what all I have checked out or on my branch
-# input is in:
-# $_[0] = project
-# $_[1] = flavor
-# $_[2] = spec line
-#
-# output:
-# return a \n serperated list of elements checked out
-sub CTCcaseIHave {
-    &CTUDebug( "in CTCcaseIHave\n" ) ;
-    local( $ret ) = "" ;
-    local( $branch ) = &CTAttachComputeBranch( $_[2], $_[1], $_[0] ) ;
-    local( $root ) = &CTProjRoot( $_[0] ) ;
-    local( *OUTPUT ) ;
-    open( OUTPUT, "cleartool find " . $root . " -element \"brtype(" .
-	  $branch . ")\" -nxn -print |" ) ;
-    while ( <OUTPUT> ) {
-	$ret = $ret . $_ ;
-    }
-    close( OUTPUT ) ;
-    &CTUDebug( "out of CTCcaseIHave\n" ) ;
-    $ret ;
-}
-
-# remove a versioned element
-# input is in:
-# $_[0] = element to remove
-# $_[1] = curr dir
-#
-# output:
-# return success or failure
-sub CTCcaseRmElem {
-    &CTUDebug( "in CTCcaseRmElem\n" ) ;
-    local( $ret ) = 0 ;
-    local( $elem ) = $_[0] ;
-    if ( ! ( $elem =~ /^\// )) {
-	$elem = $_[1] . "/" . $elem ;
-    }
-    # first we have to check out the parent directory
-    local( @alist ) = split( /\//, $elem ) ;
-    pop( @alist ) ;
-    local( $parent ) = join( "/", @alist ) ;
-    &CTUDebug( "parent directory of '" . $elem . "' is '" . $parent . "'\n" ) ;
-    $ret = system( "cleartool co -nc $parent\n" ) ;
-    if ( $ret == 0 ) {
-	# now nuke the element
-	$ret = &CTURetCode( system( "cleartool rmname $elem\n" )) ;
-    } else {
-	$ret = 0 ;
-    }
-    &CTUDebug( "out of CTCcaseRmElem\n" ) ;
-    $ret ;
-}
-
-# mv a versioned element from one name to another
-# input is in:
-# $_[0] = from element
-# $_[1] = to element
-# $_[2] = current directory
-#
-# output:
-# return success or failure
-sub CTCcaseMv {
-    &CTUDebug( "in CTCcaseMv\n" ) ;
-    local( $ret ) = 0 ;
-    local( $elem ) = $_[0] ;
-    if ( ! ( $elem =~ /^\// )) {
-	$elem = $_[2] . "/" . $elem ;
-    }
-    # first we have to check out the parent directory
-    local( @alist ) = split( /\//, $elem ) ;
-    pop( @alist ) ;
-    local( $parent ) = join( "/", @alist ) ;
-    &CTUDebug( "parent directory of '" . $elem . "' is '" . $parent . "'\n" ) ;
-    local( $elem2 ) = $_[1] ;
-    if ( ! ( $elem2 =~ /^\// )) {
-	$elem2 = $_[2] . "/" . $elem2 ;
-    }
-    local( @alist ) = split( /\//, $elem2 ) ;
-    pop( @alist ) ;
-    local( $parent2 ) = join( "/", @alist ) ;
-    &CTUDebug( "parent directory of '" . $elem2 . "' is '" . $parent2 .
-	       "'\n" ) ;
-    system( "cleartool co -nc $parent\n" ) ;
-    system( "cleartool co -nc $parent2\n" ) ;
-    $ret = &CTURetCode( system( "cleartool mv $elem $elem2\n" )) ;
-    &CTUDebug( "out of CTCcaseMv\n" ) ;
-    $ret ;
-}
-
-# build a list of targets
-# input is in:
-# $_[0] = targets
-#
-# output:
-# return success or failure
-sub CTCcaseMake {
-    &CTUDebug( "in CTCcaseMake\n" ) ;
-    local( $ret ) = 0 ;
-    local( $line ) = "clearmake -C gnu " . $_[0] .
-	" |& grep -v \"^clearmake: Warning: Config\"\n" ;
-    &CTUDebug( "line = '" . $line . "'\n" ) ;
-    $ret = &CTURetCode( system( $line )) ;
-    &CTUDebug( "out of CTCcaseMake\n" ) ;
-    $ret ;
-}
-
-1;

+ 0 - 70
dtool/src/attach/ctci

@@ -1,70 +0,0 @@
-#!/usr/bin/perl
-
-sub CTCiUsage {
-    print STDERR "Usage: ctci [-c \"comment\"] [-nc] element-name [...]\n" ;
-    print STDERR "Options:\n" ;
-    print STDERR "  -c \"comment\" : provide a comment about this action\n" ;
-    print STDERR "  -nc : expect no comment on this action\n" ;
-    exit;
-}
-
-if ( $#ARGV < 0 ) {
-    &CTCiUsage ;
-}
-
-$tool = $ENV{"DTOOL"} ;
-if ( $tool eq "" ) {
-   die "Environment not configured for CTtools" ;
-}
-
-require "$tool/built/include/ctutils.pl" ;
-require "$tool/built/include/ctvspec.pl" ;
-require "$tool/built/include/ctquery.pl" ;
-require "$tool/built/include/ctproj.pl" ;
-require "$tool/built/include/ctcm.pl" ;
-
-$comment = "" ;
-
-$skip = 0 ;
-
-@files = () ;
-
-foreach $item ( @ARGV ) {
-  if ( $skip == 0 ) {
-      if ( $item eq "-nc" ) {
-          &CTUDebug( "-nc processed\n" ) ;
-      } elsif ( $item eq "-c" ) {
-          $skip = 1 ;
-      } else {
-          push( @files, $item ) ;
-          &CTUDebug( "added '" . $item . "' to files to be processed\n" ) ;
-      }
-  } elsif ( $skip == 1 ) {
-      $comment = $item ;
-      &CTUDebug( "setting comment to '" . $comment . "'\n" ) ;
-      $skip = 0 ;
-  } else {
-      &CTUDebug( "got to unknown skip value! (" . $skip . ")\n" ) ;
-      $skip = 0 ;
-  }
-}
-
-
-if ($#files < 0 ) {
-    &CTCiUsage ;
-}
-
-$projname = &CTProj ;
-$projname =~ tr/A-Z/a-z/ ;
-$flav = &CTQueryProj( $projname ) ;
-$spec = &CTResolveSpec( $projname, $flav ) ;
-
-foreach $item ( @files ) {
-    if ( -e $item ) {
-	if ( ! &CTCMCheckin( $item, $projname, $spec, $comment ) ) {
-	    print STDERR "Could not checkin '$item'\n" ;
-	}
-    } else {
-	print STDERR "No such file '$item'.\n" ;
-    }
-}

+ 0 - 579
dtool/src/attach/ctcm.pl

@@ -1,579 +0,0 @@
-require "$tool/built/include/ctvspec.pl" ;
-require "$tool/built/include/ctquery.pl" ;
-
-# given a spec line, do the 'correct' setup for it
-# input is in:
-# $_[0] = project
-# $_[1] = spec line
-# $_[2] = flavor
-sub CTCMSetup {
-    local( $type ) = &CTSpecType( $_[1] ) ;
-    if ( $type eq "vroot" ) {
-	&CTUDebug( "running setup for an atria tree\n" ) ;
-	if ( $ENV{"HAVE_ATRIA"} eq "yes" ) {
-	    require "$tool/built/include/ctccase.pl" ;
-	    &CTAttachCCSetup( $_[0], $_[1], $_[2] ) ;
-	} else {
-	    &CTUDebug( "don't HAVE_ATRIA!\n" ) ;
-	}
-	# if we don't have atria, and it's a vroot, well..
-    } elsif ( $type eq "croot" ) {
-	&CTUDebug( "running setup for CVS\n" ) ;
-	require "$tool/built/include/ctcvs.pl" ;
-	local( $serve ) = &CTCvsServerLine( $_[0], $_[1] ) ;
-	local( $thing ) = &CTCvsLogin( $serve ) ;
-	if ( ! $thing ) {
-	    print STDERR "CVS login failed given server line '" . $serve .
-		"'\n" ;
-	}
-    }
-    # no other types have any work that needs to be done at this time
-}
-
-# given a directory, make sure it's versioned
-# input is in:
-# $_[0] = directory
-# $_[1] = project
-# $_[2] = spec line
-# $_[3] = comment (optional, "" if none)
-#
-# output:
-# return success or failure
-sub CTCMMkdir {
-    &CTUDebug( "in CTCMMkdir\n" ) ;
-    local( $ret ) = 0 ;
-    # first check that the directory is in the project, and is not the root
-    local( $flav ) = &CTQueryProj( $_[1] ) ;
-    local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ;
-    local( $pwd ) = &CTUCurrDir() ;
-    local( $isok ) = 0 ;
-    if ( $_[0] =~ /^\// ) {
-	# starts with a /, might not be in the project we are
-	if (( $_[0] =~ /^$root/ ) && ( $_[0] ne $root )) {
-	    $isok = 1 ;
-	} else {
-	    $isok = 0 ;
-	}
-    } else {
-	# are we sitting in the project?
-	if ( $pwd =~ /^$root/ ) {
-	    $isok = 1 ;
-	} else {
-	    $isok = 0 ;
-	}
-    }
-    if ( $isok ) {
-	# ok, it is.  Does one already exist?
-	if ( -e $_[0] ) {
-	    # already one there, nothing to do
-	    &CTUDebug( "directory '" . $_[0] . "' already exists\n" ) ;
-	    $ret = 1 ;
-	} else {
-	    # now switch off on how to actually do it
-	    local( $type ) = &CTSpecType( $_[2] ) ;
-	    if ( $type eq "vroot" ) {
-		require "$tool/built/include/ctccase.pl" ;
-		$ret = &CTCcaseMkdir( $_[0], $pwd, $_[3] ) ;
-	    } elsif ( $type eq "root" ) {
-		require "$tool/built/include/ctntool.pl" ;
-		$ret = &CTNtoolMkdir( $_[0], $pwd, $_[3] ) ;
-	    } elsif ( $type eq "croot" ) {
-		require "$tool/built/include/ctcvs.pl" ;
-		$ret = &CTCvsMkdir( $_[0], $_[1], $_[2], $_[3] ) ;
-	    } else {
-		print STDERR "CTCMMkdir::error! got invalid spec type '" .
-		    $type . "'\n" ;
-	    }
-	}
-    } else {
-	print STDERR "directory '" . $_[0] . "' not in project '" . $_[1] .
-	    "' or is the root.\n" ;
-    }
-    &CTUDebug( "out of CTCMMkdir\n" ) ;
-    $ret ;
-}
-
-# given a file, make sure it's versioned
-# input is in:
-# $_[0] = file
-# $_[1] = project
-# $_[2] = spec line
-# $_[3] = comment (optional, "" if none)
-# $_[4] = eltype (optional, "" if none)
-#
-# output:
-# return success or failure
-sub CTCMMkelem {
-    &CTUDebug( "in CTCMMkelem\n" ) ;
-    local( $ret ) = 0;
-    # first check that the directory is in the project
-    local( $flav ) = &CTQueryProj( $_[1] ) ;
-    local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ;
-    local( $isok ) = 0 ;
-    local( $pwd ) = &CTUCurrDir() ;
-    # synth an eltype if there is none
-    if ( ! -e $_[0] ) {
-	# need it to already exist
-	$isok = 0 ;
-    } else {
-	if ( -d $_[0] ) {
-	    # wrong command for a directory
-	    $isok = 0 ;
-	} else {
-	    if ( $_[0] =~ /^\// ) {
-		# starts with a /, might not be in the project we are
-		if ( $_[0] =~ /^$root/ ) {
-		    $isok = 1 ;
-		} else {
-		    $isok = 0 ;
-		}
-	    } else {
-		# are we sitting in the project?
-		if ( $pwd =~ /^$root/ ) {
-		    $isok = 1 ;
-		} else {
-		    $isok = 0 ;
-		}
-	    }
-	}
-    }
-    if ( $isok ) {
-	# now switch off on how to actually do the work
-	local( $type ) = &CTSpecType( $_[2] ) ;
-	if ( $type eq "vroot" ) {
-	    require "$tool/built/include/ctccase.pl" ;
-	    $ret = &CTCcaseMkelem( $_[0], $pwd, $_[3], $_[4] ) ;
-	} elsif ( $type eq "root" ) {
-	    require "$tool/built/include/ctntool.pl" ;
-	    $ret = &CTNtoolMkelem( $_[0], $pwd, $_[3], $_[4] ) ;
-	} elsif ( $type eq "croot" ) {
-	    require "$tool/built/include/ctcvs.pl" ;
-	    $ret = &CTCvsMkelem( $_[0], $_[1], $_[2], $_[3] ) ;
-	} else {
-	    print STDERR "CTCMMkelem::error! got invalid spec type '" .
-		$type . "'\n" ;
-	}
-    }
-    &CTUDebug( "out of CTCMMkelem\n" ) ;
-    $ret ;
-}
-
-# given an element, delta it in
-# input is in:
-# $_[0] = element
-# $_[1] = project
-# $_[2] = spec line
-#
-# output:
-# return success or failure
-sub CTCMDelta {
-    &CTUDebug( "in CTCMDelta\n" ) ;
-    local( $ret ) = 0 ;
-    # first check that the element is in the project
-    local( $flav ) = &CTQueryProj( $_[1] ) ;
-    local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ;
-    local( $pwd ) = &CTUCurrDir() ;
-    local( $isok ) = 0 ;
-    if ( ! -e $_[0] ) {
-	# can't delta something that doesn't exist
-	$isok = 0 ;
-    } else {
-	if ( $_[0] =~ /^\// ) {
-	    # starts with a /, might not be in the project we are
-	    if ( $_[0] =~ /^$root/ ) {
-		$isok = 1 ;
-	    } else {
-		$isok = 0 ;
-	    }
-	} else {
-	    # are we sitting in the project?
-	    if ( $pwd =~ /^$root/ ) {
-		$isok = 1 ;
-	    } else {
-		$isok = 0 ;
-	    }
-	}
-    }
-    if ( $isok ) {
-	# now switch off on how to actually do the work
-	local( $type ) = &CTSpecType( $_[2] ) ;
-	if ( $type eq "vroot" ) {
-	    require "$tool/built/include/ctccase.pl" ;
-	    $ret = &CTCcaseDelta( $_[0] ) ;
-	} elsif ( $type eq "root" ) {
-	    require "$tool/built/include/ctntool.pl" ;
-	    $ret = &CTNtoolDelta( $_[0] ) ;
-	} elsif ( $type eq "croot" ) {
-	    require "$tool/built/include/ctcvs.pl" ;
-	    $ret = &CTCvsDelta( $_[0], $_[1], $_[2] ) ;
-	} else {
-	    print STDERR "CTCMDelta::error! got invalid spec type '" . $type .
-		"'\n" ;
-	}
-    } else {
-	&CTUDebug( "failed delta pre-checks\n" ) ;
-    }
-    &CTUDebug( "out of CTCMDelta\n" ) ;
-    $ret ;
-}
-
-# given an element, check it out
-# input is in:
-# $_[0] = element
-# $_[1] = project
-# $_[2] = spec line
-# $_[3] = comment (optional, "" if none)
-#
-# output:
-# return success or failure
-sub CTCMCheckout {
-    &CTUDebug( "in CTCMCheckout\n" ) ;
-    local( $ret ) = 0 ;
-    # first check that the element is in the project
-    local( $flav ) = &CTQueryProj( $_[1] ) ;
-    local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ;
-    local( $pwd ) = &CTUCurrDir() ;
-    local( $isok ) = 0 ;
-    if ( ! -e $_[0] ) {
-	# can't checkout something that doesn't exist
-	$isok = 0 ;
-    } else {
-	if ( $_[0] =~ /^\// ) {
-	    # starts with a /, might not be in the project we are
-	    if ( $_[0] =~ /^$root/ ) {
-		$isok = 1 ;
-	    } else {
-		$isok = 0 ;
-	    }
-	} else {
-	    # are we sitting in the project?
-	    if ( $pwd =~ /^$root/ ) {
-		$isok = 1 ;
-	    } else {
-		$isok = 0 ;
-	    }
-	}
-    }
-    if ( $isok ) {
-	# now switch off on how to actually do the work
-	local( $type ) = &CTSpecType( $_[2] ) ;
-	if ( $type eq "vroot" ) {
-	    require "$tool/built/include/ctccase.pl" ;
-	    $ret = &CTCcaseCheckout( $_[0], $_[3] ) ;
-	} elsif ( $type eq "root" ) {
-	    require "$tool/built/include/ctntool.pl" ;
-	    $ret = &CTNtoolCheckout( $_[0], $_[3] ) ;
-	} elsif ( $type eq "croot" ) {
-	    require "$tool/built/include/ctcvs.pl" ;
-	    $ret = &CTCvsCheckout( $_[0], $_[1], $_[2], $_[3] ) ;
-	} else {
-	    print STDERR "CTCMCheckout::error! got invalid spec type '" .
-		$type . "'\n" ;
-	}
-    }
-    &CTUDebug( "out of CTCMCheckout\n" ) ;
-    $ret ;
-}
-
-# given an element, check it in
-# input is in:
-# $_[0] = element
-# $_[1] = project
-# $_[2] = spec line
-# $_[3] = comment (optional, "" if none)
-#
-# output:
-# return success or failure
-sub CTCMCheckin {
-    &CTUDebug( "in CTCMCheckin\n" ) ;
-    local( $ret ) = 0 ;
-    # first check that the element is in the project
-    local( $flav ) = &CTQueryProj( $_[1] ) ;
-    local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ;
-    local( $pwd ) = &CTUCurrDir() ;
-    local( $isok ) = 0 ;
-    if ( ! -e $_[0] ) {
-	# can't checkin something that doesn't exist
-	$isok = 0 ;
-    } else {
-	if ( $_[0] =~ /^\// ) {
-	    # starts with a /, might not be in the project we are
-	    if ( $_[0] =~ /^$root/ ) {
-		$isok = 1 ;
-	    } else {
-		$isok = 0 ;
-	    }
-	} else {
-	    # are we sitting in the project?
-	    if ( $pwd =~ /^$root/ ) {
-		$isok = 1 ;
-	    } else {
-		$isok = 0 ;
-	    }
-	}
-    }
-    if ( $isok ) {
-	# now switch off on how to actually do the work
-	local( $type ) = &CTSpecType( $_[2] ) ;
-	if ( $type eq "vroot" ) {
-	    require "$tool/built/include/ctccase.pl" ;
-	    $ret = &CTCcaseCheckin( $_[0], $_[3] ) ;
-	} elsif ( $type eq "root" ) {
-	    require "$tool/built/include/ctntool.pl" ;
-	    $ret = &CTNtoolCheckin( $_[0], $_[3] ) ;
-	} elsif ( $type eq "croot" ) {
-	    require "$tool/built/include/ctcvs.pl" ;
-	    $ret = &CTCvsCheckin( $_[0], $_[1], $_[2], $_[3] ) ;
-	} else {
-	    print STDERR "CTCMCheckin::error! got invalid spec type '" .
-		$type . "'\n" ;
-	}
-    }
-    &CTUDebug( "out of CTCMCheckin\n" ) ;
-    $ret ;
-}
-
-# given an element, uncheck it out
-# input is in:
-# $_[0] = element
-# $_[1] = project
-# $_[2] = spec line
-#
-# output:
-# return success or failure
-sub CTCMUncheckout {
-    &CTUDebug( "in CTCMUncheckout\n" ) ;
-    local( $ret ) = 0 ;
-    # first check that the element is in the project
-    local( $flav ) = &CTQueryProj( $_[1] ) ;
-    local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ;
-    local( $pwd ) = &CTUCurrDir() ;
-    local( $isok ) = 0 ;
-    if ( ! -e $_[0] ) {
-	# can't uncheckout something that doesn't exist
-	$isok = 0 ;
-    } else {
-	if ( $_[0] =~ /^\// ) {
-	    # starts with a /, might not be in the project we are
-	    if ( $_[0] =~ /^$root/ ) {
-		$isok = 1 ;
-	    } else {
-		$isok = 0 ;
-	    }
-	} else {
-	    # are we sitting in the project?
-	    if ( $pwd =~ /^$root/ ) {
-		$isok = 1 ;
-	    } else {
-		$isok = 0 ;
-	    }
-	}
-    }
-    if ( $isok ) {
-	# now switch off on how to actually do the work
-	local( $type ) = &CTSpecType( $_[2] ) ;
-	if ( $type eq "vroot" ) {
-	    require "$tool/built/include/ctccase.pl" ;
-	    $ret = &CTCcaseUncheckout( $_[0] ) ;
-	} elsif ( $type eq "root" ) {
-	    require "$tool/built/include/ctntool.pl" ;
-	    $ret = &CTNtoolUncheckout( $_[0] ) ;
-	} elsif ( $type eq "croot" ) {
-	    require "$tool/built/include/ctcvs.pl" ;
-	    $ret = &CTCvsUncheckout( $_[0], $_[1], $_[2] ) ;
-	} else {
-	    print STDERR "CTCMUncheckout::error! got invalid spec type '" .
-		$type . "'\n" ;
-	}
-    }
-    &CTUDebug( "out of CTCMUncheckout\n" ) ;
-    $ret ;
-}
-
-# figure out what all I have checked out in a project
-# input is in:
-# $_[0] = project
-# $_[1] = flavor
-# $_[2] = spec line
-#
-# output:
-# return a \n serperated list of elements checked out
-sub CTCMIHave {
-    &CTUDebug( "in CTCMIHave\n" ) ;
-    local( $ret ) = "" ;
-    local( $type ) = &CTSpecType( $_[2] ) ;
-    if ( $type eq "vroot" ) {
-	require "$tool/built/include/ctccase.pl" ;
-	$ret = &CTCcaseIHave( $_[0], $_[1], $_[2] ) ;
-    } elsif ( $type eq "root" ) {
-	require "$tool/built/include/ctntool.pl" ;
-	$ret = &CTNtoolIHave( $_[0], $_[1], $_[2] ) ;
-    } elsif ( $type eq "croot" ) {
-	require "$tool/built/include/ctcvs.pl" ;
-	$ret = &CTCvsIHave( $_[0], $_[1], $_[2] ) ;
-    } else {
-	print STDERR "CTCMIHave::error! got invalid spec type '" . $type .
-	    "'\n" ;
-    }
-    &CTUDebug( "out of CTCMIHave\n" ) ;
-    $ret ;
-}
-
-# given an element, remove it from the repository
-# input is in:
-# $_[0] = element
-# $_[1] = project
-# $_[2] = spec line
-#
-# output:
-# return success or failure
-sub CTCMRmElem {
-    &CTUDebug( "in CTCMRmElem\n" ) ;
-    local( $ret ) = 0 ;
-    # first check that the element is in the project
-    local( $flav ) = &CTQueryProj( $_[1] ) ;
-    local( $root ) = &CTComputeRoot( $_[1], $flav, $_[2] ) ;
-    local( $pwd ) = &CTUCurrDir() ;
-    local( $isok ) = 0 ;
-    if ( ! -e $_[0] ) {
-	# can't rmname something that doesn't exist
-	$isok = 0 ;
-    } else {
-	if ( $_[0] =~ /^\// ) {
-	    # starts with a /, might not be in the project we are
-	    if ( $_[0] =~ /^$root/ ) {
-		$isok = 1 ;
-	    } else {
-		$isok = 0 ;
-	    }
-	} else {
-	    # are we sitting in the project?
-	    if ( $pwd =~ /^$root/ ) {
-		$isok = 1 ;
-	    } else {
-		$isok = 0 ;
-	    }
-	}
-    }
-    if ( $isok ) {
-	# now switch off on how to actually do the work
-	local( $type ) = &CTSpecType( $_[2] ) ;
-	if ( $type eq "vroot" ) {
-	    require "$tool/built/include/ctccase.pl" ;
-	    $ret = &CTCcaseRnElem( $_[0], $pwd ) ;
-	} elsif ( $type eq "root" ) {
-	    require "$tool/built/include/ctntool.pl" ;
-	    $ret = &CTNtoolRmElem( $_[0], $pwd ) ;
-	} elsif ( $type eq "croot" ) {
-	    require "$tool/built/include/ctcvs.pl" ;
-	    $ret = &CTCvsRmElem( $_[0], $_[1], $_[2] ) ;
-	} else {
-	    print STDERR "CTCMRmElem::error! got invalid spec type '" .
-		$type . "'\n" ;
-	}
-    }
-    &CTUDebug( "out of CTCMRmElem\n" ) ;
-    $ret ;
-}
-
-# move an element from one name to another
-# input is in:
-# $_[0] = from element
-# $_[1] = to element
-# $_[2] = project
-# $_[3] = spec line
-#
-# output:
-# return success or failure
-sub CTCMMv {
-    &CTUDebug( "in CTCMMv\n" ) ;
-    local( $ret ) = 0 ;
-    # first check that the from and to are in the project
-    local( $flav ) = &CTQueryProj( $_[2] ) ;
-    local( $root ) = &CTComputeRoot( $_[2], $flav, $_[3] ) ;
-    local( $pwd ) = &CTUCurrDir() ;
-    local( $isok ) = 0 ;
-    if ( $_[0] =~ /^\// ) {
-	# starts with a /, might not be in the project we are
-	if ( $_[0] =~ /^$root/ ) {
-	    $isok = 1 ;
-	} else {
-	    $isok = 0 ;
-	}
-    } else {
-	# are we sitting in the project?
-	if ( $pwd =~ /^$root/ ) {
-	    $isok = 1 ;
-	} else {
-	    $isok = 0 ;
-	}
-    }
-    if ( $isok ) {
-	if ( $_[1] =~ /^\// ) {
-	    # starts with a /, might not be in the project we are
-	    if ( $_[1] =~ /^$root/ ) {
-		$isok = 1 ;
-	    } else {
-		$isok = 0 ;
-	    }
-	} else {
-	    # are we sitting in the project?
-	    if ( $pwd =~ /^$root/ ) {
-		$isok = 1 ;
-	    } else {
-		$isok = 0 ;
-	    }
-	}
-    }
-    if ( $isok ) {
-	# now switch off on how to actually do the work
-	local( $type ) = &CTSpecType( $_[3] ) ;
-	if ( $type eq "vroot" ) {
-	    require "$tool/built/include/ctccase.pl" ;
-	    $ret = &CTCcaseMv( $_[0], $_[1], $pwd ) ;
-	} elsif ( $type eq "root" ) {
-	    require "$tool/built/include/ctntool.pl" ;
-	    $ret = &CTNtoolMv( $_[0], $_[1], $pwd ) ;
-	} elsif ( $type eq "croot" ) {
-	    require "$tool/built/include/ctcvs.pl" ;
-	    $ret = &CTCvsMv( $_[0], $_[1], $_[2], $_[3] ) ;
-	} else {
-	    print STDERR "CTCMMv::error! got invalid spec type '" .
-		$type . "'\n" ;
-	}
-    }
-    &CTUDebug( "out of CTCMMv\n" ) ;
-    $ret ;
-}
-
-# give a list of targets, build them
-# input is in:
-# $_[0] = targets
-# $_[1] = project
-# $_[2] = spec line
-#
-# output:
-# return success or failure
-sub CTCMMake {
-    &CTUDebug( "in CTCMMake\n" ) ;
-    local( $ret ) = 0 ;
-    # now switch off on how to actually do the work
-    local( $type ) = &CTSpecType( $_[2] ) ;
-    if ( $type eq "vroot" ) {
-	require "$tool/built/include/ctccase.pl" ;
-	$ret = &CTCcaseMake( $_[0] ) ;
-    } elsif ( $type eq "root" ) {
-	require "$tool/built/include/ctntool.pl" ;
-	$ret = &CTNtoolMake( $_[0] ) ;
-    } elsif ( $type eq "croot" ) {
-	require "$tool/built/include/ctcvs.pl" ;
-	$ret = &CTCvsMake( $_[0] ) ;
-    } else {
-	print STDERR "CTCMMake::error! got invalid spec type '" . $type .
-	    "'\n" ;
-    }
-    &CTUDebug( "out of CTCMMake\n" ) ;
-    $ret ;
-}
-
-1;

+ 0 - 69
dtool/src/attach/ctco

@@ -1,69 +0,0 @@
-#!/usr/bin/perl
-
-sub CTCoUsage {
-    print STDERR "Usage: ctco [-c \"comment\"] [-nc] element-name [...]\n" ;
-    print STDERR "Options:\n" ;
-    print STDERR "  -c \"comment\" : provide a comment about this action\n" ;
-    print STDERR "  -nc : expect no comment on this action\n" ;
-    exit;
-}
-
-if ( $#ARGV < 0 ) {
-    &CTCoUsage ;
-}
-
-$tool = $ENV{"DTOOL"} ;
-if ( $tool eq "" ) {
-   die "Environment not configured for CTtools" ;
-}
-
-require "$tool/built/include/ctutils.pl" ;
-require "$tool/built/include/ctvspec.pl" ;
-require "$tool/built/include/ctquery.pl" ;
-require "$tool/built/include/ctproj.pl" ;
-require "$tool/built/include/ctcm.pl" ;
-
-$comment = "" ;
-
-$skip = 0 ;
-
-@files = () ;
-
-foreach $item ( @ARGV ) {
-  if ( $skip == 0 ) {
-      if ( $item eq "-nc" ) {
-          &CTUDebug( "-nc processed\n" ) ;
-      } elsif ( $item eq "-c" ) {
-          $skip = 1 ;
-      } else {
-          push( @files, $item ) ;
-          &CTUDebug( "added '" . $item . "' to files to be processed\n" ) ;
-      }
-  } elsif ( $skip == 1 ) {
-      $comment = $item ;
-      &CTUDebug( "setting comment to '" . $comment . "'\n" ) ;
-      $skip = 0 ;
-  } else {
-      &CTUDebug( "got to unknown skip value! (" . $skip . ")\n" ) ;
-      $skip = 0 ;
-  }
-}
-
-if ( $#files < 0 ) {
-    &CTCoUsage ;
-}
-
-$projname = &CTProj ;
-$projname =~ tr/A-Z/a-z/ ;
-$flav = &CTQueryProj( $projname ) ;
-$spec = &CTResolveSpec( $projname, $flav ) ;
-
-foreach $item ( @files ) {
-    if ( -e $item ) {
-	if ( ! &CTCMCheckout( $item, $projname, $spec, $comment ) ) {
-	    print STDERR "Could not checkout '$item'\n" ;
-	}
-    } else {
-	print STDERR "No such file '$item'.\n" ;
-    }
-}

+ 0 - 451
dtool/src/attach/ctcvs.pl

@@ -1,451 +0,0 @@
-# given a possibly empty string, format it into a comment or -nc
-# input is in:
-# $_[0] = possible comment string
-#
-# output is:
-# string for use by CVS functions
-sub CTCvsFormatComment {
-    local( $ret ) = "" ;
-    if ( $_[0] ne "" ) {
-        $ret = "-m \"" . $_[0] . "\"" ;
-    }
-    $ret ;
-}
-
-# given a project and spec line, compute the server line
-# input is in:
-# $_[0] = project
-# $_[1] = spec line
-#
-# output:
-# return a sever line, or "" if not a croot
-sub CTCvsServerLine {
-    &CTUDebug( "in CTCvsServerLine\n" ) ;
-    local( $ret ) = "" ;
-    local( $type ) = &CTSpecType( $_[1] ) ;
-    if ( $type eq "croot" ) {
-	local( $options ) = &CTSpecOptions( $_[1] ) ;
-	local( $sline ) = &CTSpecFindOption( $options, "server" ) ;
-	if ( $sline ne "" ) {
-	    $ret = join( ":", split( /,/, $sline ));
-	}
-    }
-    &CTUDebug( "out of CTCvsServerLine\n" ) ;
-    $ret ;
-}
-
-# if needed log into a cvs server
-# input is in:
-# $_[0] = server line
-#
-# output:
-# return success or failure
-sub CTCvsLogin {
-    &CTUDebug( "in CTCvsLogin\n" ) ;
-    local( $ret ) = 0 ;
-    &CTUDebug( "server line is '" . $_[0] . "'\n" ) ;
-    if ( $_[0] ne "" ) {
-	# ok.  we actually have something, lets look in .cvspass
-	local( $path ) ;
-	local( *PASSFILE ) ;
-	if ( $ENV{"PENV"} eq "WIN32" ) {
-	    $path = $ENV{"HOME"} . "/.cvspass" ;
-	} else {
-	    # $path = "~/.cvspass" ;
-	    $path = $ENV{"HOME"} . "/.cvspass" ;
-	}
-	&CTUDebug( "looking for '" . $path . "'\n" ) ;
-	if ( -e $path ) {
-	    local( $passdone ) = 0 ;
-	    local( $ok ) = 0 ;
-	    open( PASSFILE, "< $path" ) ;
-	    while ( <PASSFILE> ) {
-		s/\n$// ;
-		local( @line ) = split ;
-		# ok, the server line is in [0] and the password in [1].
-		&CTUDebug( "server line from .cvspass is '" . $line[0] .
-			   "'\n" ) ;
-		if ( $line[0] eq $_[0] ) {
-		    # we're fine, we're already logged in to that
-		    $ret = 1 ;
-		    $passdone = 1;
-		}
-	    }
-	    if ( ! $passdone ) {
-		# ran out of lines in the file
-		local( $line ) = "cvs -d " . $_[0] . " login >/dev/null" ;
-		&CTUDebug( "about to run '" . $line . "'\n" ) ;
-		$ret = &CTURetCode( system( $line )) ;
-	    }
-	} else {
-	    &CTUDebug( $path . " file does not exist\n" ) ;
-	    local( $line ) = "cvs -d " . $_[0] . " login >/dev/null" ;
-	    &CTUDebug( "about to run '" . $line . "'\n" ) ;
-	    $ret = &CTURetCode( system( $line )) ;
-	}
-    }
-    &CTUDebug( "out of CTCvsLogin\n" ) ;
-    $ret ;
-}
-
-require "$tool/built/include/ctproj.pl" ;
-
-# add a versioned element to the repository
-# input is in:
-# $_[0] = element
-# $_[1] = project
-# $_[2] = spec line
-# $_[3] = possible comment
-#
-# output:
-# return success or failure
-sub CTCvsAdd {
-    &CTUDebug( "in CTCvsAdd\n" ) ;
-    # first we need to 'login' to the repository
-    local( $comment ) = &CTCvsFormatComment( $_[3] ) ;
-    local( $serve ) = &CTCvsServerLine( $_[1], $_[2] ) ;
-    local( $ret ) = &CTCvsLogin( $serve ) ;
-    if ( $ret ) {
-	# now issue the add command
-	local( $root ) = &CTProjRoot( $_[1] ) ;
-	local( $line ) = "" ;
-	local( $elem ) = $_[0] ;
-	if ( $elem =~ /^\// ) {
-	    local( $proj ) = $_[1] ;
-	    $proj =~ tr/a-z/A-Z/ ;
-	    $line = "cd \$" . $proj . "; " ;
-	    $elem =~ s/^$root\/// ;
-	}
-	$line = $line . "cvs -d " . $serve . " add " . $comment . " $elem" ;
-	&CTUDebug( "about to execute '" . $line . "'\n" ) ;
-	$ret = &CTURetCode( system( $line )) ;
-    }
-    &CTUDebug( "out of CTCvsAdd\n" ) ;
-    $ret ;
-}
-
-# ci a versioned element to the repository
-# input is in:
-# $_[0] = element
-# $_[1] = project
-# $_[2] = spec line
-# $_[3] = possible comment
-#
-# output:
-# return success or failure
-sub CTCvsCi {
-    &CTUDebug( "in CTCvsCi\n" ) ;
-    # first we need to 'login' to the repository
-    local( $comment ) = &CTCvsFormatComment( $_[3] ) ;
-    local( $serve ) = &CTCvsServerLine( $_[1], $_[2] ) ;
-    local( $ret ) = &CTCvsLogin( $serve ) ;
-    if ( $ret ) {
-	# now issue the add command
-	local( $root ) = &CTProjRoot( $_[1] ) ;
-	local( $line ) = "" ;
-	local( $elem ) = $_[0] ;
-	if ( $elem =~ /^\// ) {
-	    local ( $proj ) = $_[1] ;
-	    $proj =~ tr/a-z/A-Z/ ;
-	    $line = "cd \$" . $proj . "; " ;
-	    $elem =~ s/^$root\/// ;
-	}
-	$line = $line . "cvs -d " . $serve . " ci " . $comment . " $elem" ;
-	&CTUDebug( "about to execute '" . $line . "'\n" ) ;
-	$ret = &CTURetCode( system( $line )) ;
-    }
-    &CTUDebug( "out of CTCvsCi\n" ) ;
-    $ret ;
-}
-
-# rm a versioned element from the repository
-# input is in:
-# $_[0] = element
-# $_[1] = project
-# $_[2] = spec line
-#
-# output:
-# return success or failure
-sub CTCvsRm {
-    &CTUDebug( "in CTCvsRm\n" ) ;
-    # first we need to 'login' to the repository
-    local( $serve ) = &CTCvsServerLine( $_[1], $_[2] ) ;
-    local( $ret ) = &CTCvsLogin( $serve ) ;
-    if ( $ret ) {
-	# now issue the add command
-	$ret = &CTURetCode( system( "cvs -d " . $serve . " rm $_[0]\n" )) ;
-    }
-    &CTUDebug( "out of CTCvsRm\n" ) ;
-    $ret ;
-}
-
-# make a versioned directory
-# input is in:
-# $_[0] = directory to create
-# $_[1] = project
-# $_[2] = spec line
-# $_[3] = possible comment
-#
-# output:
-# return success or failure
-sub CTCvsMkdir {
-    &CTUDebug( "in CTCvsMkdir\n" ) ;
-    local( $ret ) = 0 ;
-    # first make the dir
-    $ret = &CTURetCode( system( "mkdir $_[0]\n" )) ;
-    if ( $ret ) {
-	# now version it
-	$ret = &CTCvsAdd( $_[0], $_[1], $_[2], $_[3] ) ;
-    } else {
-	&CTUDebug( "could not create directory '" . $_[0] . "'\n" ) ;
-	$ret = 0 ;
-    }
-    &CTUDebug( "out of CTCvsMkdir\n" ) ;
-    $ret ;
-}
-
-# make a versioned element
-# input is in:
-# $_[0] = element to version
-# $_[1] = project
-# $_[2] = spec line
-# $_[3] = possible comment
-#
-# output:
-# return success or failure
-sub CTCvsMkelem {
-    &CTUDebug( "in CTCvsMkelem\n" ) ;
-    # first cvs add the file
-    local( $ret ) = &CTCvsAdd( $_[0], $_[1], $_[2], $_[3] ) ;
-    if ( $ret ) {
-	# now commit it
-	$ret = &CTCvsCi( $_[0], $_[1], $_[2], $_[3] ) ;
-    } else {
-	&CTUDebug( "could not CVS add '" . $_[0] . "'\n" ) ;
-	$ret = 0 ;
-    }
-    &CTUDebug( "out of CTCvsMkelem\n" ) ;
-    $ret ;
-}
-
-# delta an element
-# input is in:
-# $_[0] = element to delta
-# $_[1] = project
-# $_[2] = spec line
-#
-# output:
-# return success or failure
-sub CTCvsDelta {
-    &CTUDebug( "in CTCvsDelta\n" ) ;
-    local( $ret ) = 0 ;
-    # for lack of better idea, this is going to be just checkin for now
-    if ( -d $_[0] ) {
-	# we don't version directories in CVS
-	$ret = 1 ;
-    } else {
-	$ret = &CTCvsCi( $_[0], $_[1], $_[2] ) ;
-    }
-    &CTUDebug( "out of CTCvsDelta\n" ) ;
-    $ret ;
-}
-
-# checkout an element
-# input is in:
-# $_[0] = element to checkout
-# $_[1] = project
-# $_[2] = spec line
-# $_[3] = possible comment
-#
-# output:
-# return success or failure
-sub CTCvsCheckout {
-    &CTUDebug( "in CTCvsCheckout\n" ) ;
-    local( $ret ) = 1 ;
-    # for my limited understanding of CVS, there doesn't seem to be any
-    # 'checkout' for it.
-    &CTUDebug( "out of CTCvsCheckout\n" ) ;
-    $ret ;
-}
-
-# checkin an element
-# input is in:
-# $_[0] = element to checkin
-# $_[1] = project
-# $_[2] = spec line
-# $_[3] = possible comment
-#
-# output:
-# return success or failure
-sub CTCvsCheckin {
-    &CTUDebug( "in CTCvsCheckin\n" ) ;
-    local( $ret ) = 0 ;
-    if ( -d $_[0] ) {
-	# we don't version directories in CVS
-	$ret = 1 ;
-    } else {
-	$ret = &CTCvsCi( $_[0], $_[1], $_[2], $_[3] ) ;
-    }
-    &CTUDebug( "out of CTCvsCheckin\n" ) ;
-    $ret ;
-}
-
-# uncheckout an element
-# input is in:
-# $_[0] = element to uncheckout
-# $_[1] = project
-# $_[2] = spec line
-#
-# output:
-# return success or failure
-sub CTCvsUncheckout {
-    &CTUDebug( "in CTCvsUncheckout\n" ) ;
-    local( $ret ) = 0 ;
-    if ( -d $_[0] ) {
-	# we don't version directories in CVS
-	$ret = 1 ;
-    } else {
-	$ret = &CTURetCode( system( "rm $_[0]" ) ) ;
-	if ( $ret ) {
-	    local( $serve ) = &CTCvsServerLine( $_[1], $_[2] ) ;
-	    $ret = &CTCvsLogin( $serve ) ;
-	    if ( $ret ) {
-		$ret = &CTURetCode( system( "cvs -d " . $serve . " update " .
-					    $_[0] )) ;
-	    }
-	}
-    }
-    &CTUDebug( "out of CTCvsUncheckout\n" ) ;
-    $ret ;
-}
-
-# figure out what all I have checked out
-# input is in:
-# $_[0] = project
-# $_[1] = flavor
-# $_[2] = spec line
-#
-# output:
-# return a \n serperated list of elements checked out
-sub CTCvsIHave {
-    &CTUDebug( "in CTCvsIHave\n" ) ;
-    local( $ret ) = "" ;
-    local( $proj ) = $_[0] ;
-    $proj =~ tr/a-z/A-Z/ ;
-    local( $line ) = "cd \$" . $proj . "; " ;
-    local( $serve ) = &CTCvsServerLine( $_[0], $_[2] ) ;
-    local( $ok ) = &CTCvsLogin( $serve ) ;
-    if ( $ok ) {
-	$line = $line . "cvs -n -d " . $serve . " update 2>/dev/null" ;
-	local( $hold ) = "";
-	local( *OUTPUT ) ;
-	open( OUTPUT, $line . " |" ) ;
-	while ( <OUTPUT> ) {
-	    $hold = $hold . $_ ;
-	}
-	close( OUTPUT ) ;
-	local( @lines ) = split( /\n/, $hold ) ;
-	local( $item ) ;
-	foreach $item ( @lines ) {
-	    if ( $item =~ /^\?/ ) {
-		# things that start with a ? are ignored
-	    } elsif ( $item =~ /^cvs/ ) {
-		# messages from the server are also ignored
-	    } elsif ( $item =~ /^P/ ) {
-		# new files are ignored
-	    } elsif ( $item =~ /^U/ ) {
-		# updates are ignored
-	    } elsif ( $item =~ /^M/ ) {
-		# here's one we modified
-		local( @foo ) = split( / /, $item ) ;
-		$ret = $ret . $foo[1] . "\n" ;
-	    } else {
-		# don't what this means, better complain
-		local( @foo ) = split( / /, $item ) ;
-		print STDERR "got unknown update code '" . $foo[0] .
-		    "' for file '" . $foo[1] . "'\n" ;
-	    }
-	}
-    }
-    &CTUDebug( "out of CTCvsIHave\n" ) ;
-    $ret ;
-}
-
-# remove an element from the repository
-# input is in:
-# $_[0] = element to uncheckout
-# $_[1] = project
-# $_[2] = spec line
-#
-# output:
-# return success or failure
-sub CTCvsRmElem {
-    &CTUDebug( "in CTCvsRmElem\n" ) ;
-    local( $ret ) = 0 ;
-    if ( -d $_[0] ) {
-	# CVS doesn't really do this.  If there are no files in the directory,
-	# the next time an update -P is run, it will be deleted.
-	$ret = 1 ;
-    } else {
-	$ret = &CTURetCode( system( "rm $_[0]" ) ) ;
-	if ( $ret ) {
-	    $ret = &CTCvsRm( $_[0], $_[1], $_[2] ) ;
-	    if ( $ret ) {
-		$ret = &CTCvsCi( $_[0], $_[1], $_[2] ) ;
-	    }
-	}
-    }
-    &CTUDebug( "out of CTCvsRmElem\n" ) ;
-    $ret ;
-}
-
-# move a versioned element from one name to another
-# input is in:
-# $_[0] = from element
-# $_[1] = to element
-# $_[2] = project
-# $_[3] = spec line
-#
-# output:
-# return success or failure
-sub CTCvsMv {
-    &CTUDebug( "in CTCvsMv\n" ) ;
-    local( $ret ) = 0 ;
-    if ( -d $_[0] ) {
-	# don't have code to do directories yet.  See pp 54 of the CVS book
-	$ret = 0 ;
-    } else {
-	$ret = &CTURetCode( system( "mv $_[0] $_[1]" ) ) ;
-	if ( $ret ) {
-	    $ret = &CTCvsRm( $_[0], $_[2], $_[3] ) ;
-	    if ( $ret ) {
-		$ret = &CTCvsAdd( $_[1], $_[2], $_[3] );
-		if ( $ret ) {
-		    $ret = &CTCvsCi( $_[0], $_[2], $_[3] ) ;
-		    if ( $ret ) {
-			$ret = &CTCvsCi( $_[1], $_[2], $_[3] ) ;
-		    }
-		}
-	    }
-	}
-    }
-    &CTUDebug( "out of CTCvsMv\n" ) ;
-    $ret ;
-}
-
-# build a list of targets
-# input is in:
-# $_[0] = targets
-#
-# output:
-# return success or failure
-sub CTCvsMake {
-    &CTUDebug( "in CTCvsMake\n" ) ;
-    local( $ret ) = 0 ;
-    local( $line ) = "make " . $_[0] . "\n" ;
-    $ret = &CTURetCode( system( $line )) ;
-    &CTUDebug( "out of CTCvsMake\n" ) ;
-    $ret ;
-}
-
-1;

+ 0 - 57
dtool/src/attach/ctdelta

@@ -1,57 +0,0 @@
-#!/usr/bin/perl
-
-if ($#ARGV < 0) {
-   exit print "Usage: ctdelta element-name [...]\n" ;
-}
-
-$tool = $ENV{"DTOOL"} ;
-if ( $tool eq "" ) {
-    die "not configured for using ct-tools\n" ;
-}
-
-require "$tool/built/include/ctutils.pl" ;
-require "$tool/built/include/ctvspec.pl" ;
-require "$tool/built/include/ctquery.pl" ;
-require "$tool/built/include/ctproj.pl" ;
-require "$tool/built/include/ctcm.pl" ;
-
-$comment = "" ;
-$skip = 0 ;
-
-@files = () ;
-
-foreach $item ( @ARGV ) {
-  if ( $skip == 0 ) {
-      if ( $item eq "-nc" ) {
-          &CTUDebug( "-nc processed\n" ) ;
-      } elsif ( $item eq "-c" ) {
-          $skip = 1 ;
-      } else {
-          push( @files, $item ) ;
-          &CTUDebug( "added '" . $item . "' to files to be processed\n" ) ;
-      }
-  } elsif ( $skip == 1 ) {
-      $comment = $item ;
-      &CTUDebug( "setting comment to '" . $comment . "'\n" ) ;
-      $skip = 0 ;
-  } else {
-      &CTUDebug( "got to unknown skip value! (" . $skip . ")\n" ) ;
-      $skip = 0 ;
-  }
-}
-
-$projname = &CTProj ;
-$projname =~ tr/A-Z/a-z/ ;
-$flav = &CTQueryProj( $projname ) ;
-$spec = &CTResolveSpec( $projname, $flav ) ;
-
-foreach $item ( @files ) {
-    if ( -e $item ) {
-	&CTCMCheckin( $item, $projname, $spec ) ;
-	if ( ! &CTCMDelta( $item, $projname, $spec ) ) {
-	    print STDERR "Could not delta '$item'\n" ;
-	}
-    } else {
-	print STDERR "No such file '$item'.\n" ;
-    }
-}

+ 0 - 232
dtool/src/attach/ctdelta.pl

@@ -1,232 +0,0 @@
-# Check in element if needed
-# Input is:
-#   $_[0] = element name
-sub CTDeltaCheckin {
-   local( $cmd ) = "cleartool ci -nc $_[0] 2> /dev/null > /dev/null" ;
-   system $cmd ;
-}
-
-# get the version of an element
-# Input is:
-#   $_[0] = element name
-sub CTDeltaGetVersion {
-   local( *CMDFILE ) ;
-   open( CMDFILE, "cleartool describe -short $_[0] |" ) ;
-   $_ = <CMDFILE> ;
-   close( CMDFILE ) ;
-   s/\n$// ;
-   s/^.*@@// ;
-   s/\"$// ;
-   $_ ;
-}
-
-# Is it ok to try a merge on this version?
-# Input is:
-#   $_[0] = version
-sub CTDeltaOk {
-   local( $ret ) ;
-   local( @verlist ) ;
-   @verlist = split( /\//, $_[0] ) ;
-   pop( @verlist ) ;
-   if ( $#verlist > 1 ) {
-      $ret = 1 ;
-   } else {
-      $ret = 0 ;
-   }
-   $ret ;
-}
-
-# get the comments from a version of an element
-# Input is:
-#   $_[0] = element name
-#   $_[1] = version
-#
-# output in:
-#   @CTDeltaComments
-sub CTDeltaGetComments {
-   local( *CMDFILE ) ;
-   local( $done ) = 0 ;
-   local( $end ) = "  element type:" ;
-   local( $tmp ) = "cleartool describe $_[0]" . "@@" . "$_[1] |" ;
-   open( CMDFILE, $tmp ) ;
-   $_ = <CMDFILE> ;
-   $_ = <CMDFILE> ;
-   while ( ! $done ) {
-      $_ = <CMDFILE> ;
-      if ( $_ =~ /^$end/ ) {
-	 $done = 1 ;
-      } else {
-	 s/^  // ;
-	 s/^ // ;
-	 s/^\"// ;
-	 s/\n$// ;
-	 s/\"$// ;
-	 push( @CTDeltaComments, $_ ) ;
-      }
-   }
-   close( CMDFILE ) ;
-}
-
-# try automatic merge.  If it fails, use xmerge
-# Input is:
-#   $_[0] = element name
-#   $_[1] = source version
-#   $_[2] = target version
-sub CTDeltaSafeMerge {
-   @CTDeltaComments = ();
-   &CTDeltaGetComments($_[0], $_[1]);
-   local( $ret ) ;
-   $ret = "cleartool checkout -branch $_[2] -nc $_[0] 2> /dev/null > /dev/null" ;
-   $ret = system $ret ;
-   if ( $ret != 0 ) {
-      print STDERR "got return value $ret from checkout on '$_[0]" . "@@" . "$_[2]'\n" ;
-      exit -1;
-   }
-   local( $item ) ;
-   foreach $item ( @CTDeltaComments ) {
-      $ret = "cleartool chevent -append -c \"" . $item . "\" $_[0]" . "@@" . "$_[2]" . "/LATEST 2> /dev/null > /dev/null" ;
-      system $ret ;
-   }
-   print STDERR "merging '$_[0]'...\n" ;
-   $ret = "cleartool merge -abort -to $_[0] -version $_[1] 2> /dev/null > /dev/null" ;
-   $ret = system $ret ;
-   if ( $ret != 0 ) {
-      $ret = system "cleartool xmerge -to $_[0] -version $_[1]" ;
-   }
-   if ( ! -d $_[0] ) {
-      system "rm $_[0]" . ".contrib" ;
-   }
-   $ret ;
-}
-
-# test a branch for 'triviality'
-# Input is:
-#   $_[0] = element name
-#   $_[1] = branch name
-#
-# Output is:
-#   true/false
-sub CTDeltaTestBranch {
-   local( *CTCMD ) ;
-   local( $ret ) ;
-   local( $done ) = 0 ;
-   local( $bfrom ) ;
-   local( @blist ) ;
-   local( $bto ) ;
-   local( $bdiff ) ;
-   local( $blast ) ;
-   @blist = split( /\//, $_[1] ) ;
-   pop( @blist ) ;
-   $ret = join( "/", @blist ) ;
-   $ret = "cleartool describe $_[0]" . "@@" . "$ret |" ;
-   open( CTCMD, $ret ) ;
-   while ( ! $done ) {
-      $_ = <CTCMD> ;
-      if ( $_ =~ /^  branched from version/ ) {
-	 $done = 1 ;
-      }
-   }
-   close( CTCMD ) ;
-   s/^  branched from version: // ;
-   s/\n$// ;
-   $bfrom = $_ ;
-   @blist = split( /\//, $_ ) ;
-   pop( @blist ) ;
-   push( @blist, "LATEST" ) ;
-   $ret = join( "/", @blist ) ;
-   $ret = "cleartool describe $_[0]" . "@@" . "$ret |" ;
-   open( CTCMD, $ret ) ;
-   $_ = <CTCMD> ;
-   close( CTCMD ) ;
-   s/\n$// ;
-   s/^.*@@// ;
-   s/\"$// ;
-   $bto = $_ ;
-   @blist = split( /\//, $bfrom ) ;
-   $bfrom = pop( @blist ) ;
-   @blist = split( /\//, $bto ) ;
-   $bto = pop( @blist ) ;
-   $bdiff = $bto - $bfrom ;
-   $ret = "cleartool describe $_[0]" . "@@" . "$_[1] |" ;
-   open( CTCMD, $ret ) ;
-   $_ = <CTCMD> ;
-   close( CTCMD ) ;
-   s/\n$// ;
-   s/^.*@@// ;
-   s/\"$// ;
-   @blist = split( /\//, $_ ) ;
-   $blast = pop( @blist ) ;
-   if (( $bdiff > 1 ) || ( $blast > 1 )) {
-      $ret = 0 ;
-   } else {
-      $ret = 1 ;
-   }
-}
-
-# check for trivial branch elimination
-# Input is:
-#   $_[0] = element name
-#   $_[1] = last branch version
-#   $_[2] = timestamp string
-sub CTDeltaBranchCheck {
-   local( $test ) = &CTDeltaTestBranch( $_[0], $_[1] ) ;
-   local( $cmd ) ;
-   local( @blist ) ;
-   local( $branch ) ;
-   @blist = split( /\//, $_[1] ) ;
-   if ( $test ) {
-       pop( @blist ) ;
-       $cmd = join( "/", @blist ) ;
-       $branch = join( "/", @blist ) ;
-       $cmd = "cleartool rmbranch -force $_[0]" . "@@" . "$cmd 2> /dev/null > /dev/null" ;
-       print STDERR "deleting branch '$branch'...\n" ;
-       system $cmd ;
-   } else {
-       pop( @blist ) ;
-       $branch = join( "/", @blist ) ;
-       $test = pop( @blist ) ;
-       $test = $test . $_[2] ;
-       $cmd = "cleartool mkbrtype -c \"non-trivial branch\" $test 2> /dev/null > /dev/null" ;
-       system $cmd ;
-       $cmd = "cleartool chtype -c \"renaming non-trivial branch\" $test $_[0]" . "@@" . "$branch 2> /dev/null > /dev/null" ;
-       print STDERR "renaming branch '$branch'...\n" ;
-       system $cmd ;
-   }
-}
-
-# log merge to /usr/local/etc/delta_log
-# Input is:
-#   $_[0] = element name
-#   $_[1] = source version
-#   $_[2] = target version
-sub CTDeltaLog {
-   local( *LOGFILE ) ;
-   local( *CMDFILE ) ;
-   local( $cmd ) ;
-   open( LOGFILE, ">>/usr/local/etc/delta_log" ) ;
-   print LOGFILE $_[0] . ": " . $_[1] . " -> " . $_[2] . " : " ;
-   if ( $ctdebug ne "" ) {
-      print STDERR "CTDeltaLog: outputting '" . $_[0] . ": " . $_[1] . " -> " . $_[2] . " : '\n" ;
-   }
-   $cmd = "ypmatch `whoami` passwd | cut -d: -f5 |" ;
-   open( CMDFILE, $cmd ) ;
-   $_ = <CMDFILE> ;
-   s/\n$//;
-   print LOGFILE $_ . " " ;
-   if ( $ctdebug ne "" ) {
-      print STDERR "CTDeltaLog: outputting '" . $_ . " '\n" ;
-   }
-   close( CMDFILE ) ;
-   $cmd = "/bin/date '+%m/%d/%y %H:%M:%S' |" ;
-   open( CMDFILE, $cmd ) ;
-   $_ = <CMDFILE> ;
-   s/\n$//;
-   print LOGFILE $_ . "\n" ;
-   if ( $ctdebug ne "" ) {
-      print STDERR "CTDeltaLog: outputting '" . $_ . " '\n" ;
-   }
-   close( CMDFILE ) ;
-   close( LOGFILE ) ;
-}
-
-1;

+ 0 - 232
dtool/src/attach/ctdelta.pl.rnd

@@ -1,232 +0,0 @@
-# Check in element if needed
-# Input is:
-#   $_[0] = element name
-sub CTDeltaCheckin {
-   local( $cmd ) = "cleartool ci -nc $_[0] 2> /dev/null > /dev/null" ;
-   system $cmd ;
-}
-
-# get the version of an element
-# Input is:
-#   $_[0] = element name
-sub CTDeltaGetVersion {
-   local( *CMDFILE ) ;
-   open( CMDFILE, "cleartool describe -short $_[0] |" ) ;
-   $_ = <CMDFILE> ;
-   close( CMDFILE ) ;
-   s/\n$// ;
-   s/^.*@@// ;
-   s/\"$// ;
-   $_ ;
-}
-
-# Is it ok to try a merge on this version?
-# Input is:
-#   $_[0] = version
-sub CTDeltaOk {
-   local( $ret ) ;
-   local( @verlist ) ;
-   @verlist = split( /\//, $_[0] ) ;
-   pop( @verlist ) ;
-   if ( $#verlist > 1 ) {
-      $ret = 1 ;
-   } else {
-      $ret = 0 ;
-   }
-   $ret ;
-}
-
-# get the comments from a version of an element
-# Input is:
-#   $_[0] = element name
-#   $_[1] = version
-#
-# output in:
-#   @CTDeltaComments
-sub CTDeltaGetComments {
-   local( *CMDFILE ) ;
-   local( $done ) = 0 ;
-   local( $end ) = "  element type:" ;
-   local( $tmp ) = "cleartool describe $_[0]" . "@@" . "$_[1] |" ;
-   open( CMDFILE, $tmp ) ;
-   $_ = <CMDFILE> ;
-   $_ = <CMDFILE> ;
-   while ( ! $done ) {
-      $_ = <CMDFILE> ;
-      if ( $_ =~ /^$end/ ) {
-	 $done = 1 ;
-      } else {
-	 s/^  // ;
-	 s/^ // ;
-	 s/^\"// ;
-	 s/\n$// ;
-	 s/\"$// ;
-	 push( @CTDeltaComments, $_ ) ;
-      }
-   }
-   close( CMDFILE ) ;
-}
-
-# try automatic merge.  If it fails, use xmerge
-# Input is:
-#   $_[0] = element name
-#   $_[1] = source version
-#   $_[2] = target version
-sub CTDeltaSafeMerge {
-   @CTDeltaComments = ();
-   &CTDeltaGetComments($_[0], $_[1]);
-   local( $ret ) ;
-   $ret = "cleartool checkout -branch $_[2] -nc $_[0] 2> /dev/null > /dev/null" ;
-   $ret = system $ret ;
-   if ( $ret != 0 ) {
-      print STDERR "got return value $ret from checkout on '$_[0]" . "@@" . "$_[2]'\n" ;
-      exit -1;
-   }
-   local( $item ) ;
-   foreach $item ( @CTDeltaComments ) {
-      $ret = "cleartool chevent -append -c \"" . $item . "\" $_[0]" . "@@" . "$_[2]" . "/LATEST 2> /dev/null > /dev/null" ;
-      system $ret ;
-   }
-   print STDERR "merging '$_[0]'...\n" ;
-   $ret = "cleartool merge -abort -to $_[0] -version $_[1] 2> /dev/null > /dev/null" ;
-   $ret = system $ret ;
-   if ( $ret != 0 ) {
-      $ret = system "cleartool xmerge -to $_[0] -version $_[1]" ;
-   }
-   if ( ! -d $_[0] ) {
-      system "rm $_[0]" . ".contrib" ;
-   }
-   $ret ;
-}
-
-# test a branch for 'triviality'
-# Input is:
-#   $_[0] = element name
-#   $_[1] = branch name
-#
-# Output is:
-#   true/false
-sub CTDeltaTestBranch {
-   local( *CTCMD ) ;
-   local( $ret ) ;
-   local( $done ) = 0 ;
-   local( $bfrom ) ;
-   local( @blist ) ;
-   local( $bto ) ;
-   local( $bdiff ) ;
-   local( $blast ) ;
-   @blist = split( /\//, $_[1] ) ;
-   pop( @blist ) ;
-   $ret = join( "/", @blist ) ;
-   $ret = "cleartool describe $_[0]" . "@@" . "$ret |" ;
-   open( CTCMD, $ret ) ;
-   while ( ! $done ) {
-      $_ = <CTCMD> ;
-      if ( $_ =~ /^  branched from version/ ) {
-	 $done = 1 ;
-      }
-   }
-   close( CTCMD ) ;
-   s/^  branched from version: // ;
-   s/\n$// ;
-   $bfrom = $_ ;
-   @blist = split( /\//, $_ ) ;
-   pop( @blist ) ;
-   push( @blist, "LATEST" ) ;
-   $ret = join( "/", @blist ) ;
-   $ret = "cleartool describe $_[0]" . "@@" . "$ret |" ;
-   open( CTCMD, $ret ) ;
-   $_ = <CTCMD> ;
-   close( CTCMD ) ;
-   s/\n$// ;
-   s/^.*@@// ;
-   s/\"$// ;
-   $bto = $_ ;
-   @blist = split( /\//, $bfrom ) ;
-   $bfrom = pop( @blist ) ;
-   @blist = split( /\//, $bto ) ;
-   $bto = pop( @blist ) ;
-   $bdiff = $bto - $bfrom ;
-   $ret = "cleartool describe $_[0]" . "@@" . "$_[1] |" ;
-   open( CTCMD, $ret ) ;
-   $_ = <CTCMD> ;
-   close( CTCMD ) ;
-   s/\n$// ;
-   s/^.*@@// ;
-   s/\"$// ;
-   @blist = split( /\//, $_ ) ;
-   $blast = pop( @blist ) ;
-   if (( $bdiff > 1 ) || ( $blast > 1 )) {
-      $ret = 0 ;
-   } else {
-      $ret = 1 ;
-   }
-}
-
-# check for trivial branch elimination
-# Input is:
-#   $_[0] = element name
-#   $_[1] = last branch version
-#   $_[2] = timestamp string
-sub CTDeltaBranchCheck {
-   local( $test ) = &CTDeltaTestBranch( $_[0], $_[1] ) ;
-   local( $cmd ) ;
-   local( @blist ) ;
-   local( $branch ) ;
-   @blist = split( /\//, $_[1] ) ;
-   if ( $test ) {
-       pop( @blist ) ;
-       $cmd = join( "/", @blist ) ;
-       $branch = join( "/", @blist ) ;
-       $cmd = "cleartool rmbranch -force $_[0]" . "@@" . "$cmd 2> /dev/null > /dev/null" ;
-       print STDERR "deleting branch '$branch'...\n" ;
-       system $cmd ;
-   } else {
-       pop( @blist ) ;
-       $branch = join( "/", @blist ) ;
-       $test = pop( @blist ) ;
-       $test = $test . $_[2] ;
-       $cmd = "cleartool mkbrtype -c \"non-trivial branch\" $test 2> /dev/null > /dev/null" ;
-       system $cmd ;
-       $cmd = "cleartool chtype -c \"renaming non-trivial branch\" $test $_[0]" . "@@" . "$branch 2> /dev/null > /dev/null" ;
-       print STDERR "renaming branch '$branch'...\n" ;
-       system $cmd ;
-   }
-}
-
-# log merge to /var/etc/delta_log
-# Input is:
-#   $_[0] = element name
-#   $_[1] = source version
-#   $_[2] = target version
-sub CTDeltaLog {
-   local( *LOGFILE ) ;
-   local( *CMDFILE ) ;
-   local( $cmd ) ;
-   open( LOGFILE, ">>/var/etc/delta_log" ) ;
-   print LOGFILE $_[0] . ": " . $_[1] . " -> " . $_[2] . " : " ;
-   if ( $ctdebug ne "" ) {
-      print STDERR "CTDeltaLog: outputting '" . $_[0] . ": " . $_[1] . " -> " . $_[2] . " : '\n" ;
-   }
-   $cmd = "ypmatch `whoami` passwd | cut -d: -f5 |" ;
-   open( CMDFILE, $cmd ) ;
-   $_ = <CMDFILE> ;
-   s/\n$//;
-   print LOGFILE $_ . " " ;
-   if ( $ctdebug ne "" ) {
-      print STDERR "CTDeltaLog: outputting '" . $_ . " '\n" ;
-   }
-   close( CMDFILE ) ;
-   $cmd = "/bin/date '+%m/%d/%y %H:%M:%S' |" ;
-   open( CMDFILE, $cmd ) ;
-   $_ = <CMDFILE> ;
-   s/\n$//;
-   print LOGFILE $_ . "\n" ;
-   if ( $ctdebug ne "" ) {
-      print STDERR "CTDeltaLog: outputting '" . $_ . " '\n" ;
-   }
-   close( CMDFILE ) ;
-   close( LOGFILE ) ;
-}
-
-1;

+ 0 - 33
dtool/src/attach/ctihave

@@ -1,33 +0,0 @@
-#!/usr/bin/perl
-
-if ($#ARGV != -1) {
-   exit print "Usage: ctihave\n" ;
-}
-
-$tool = $ENV{"DTOOL"} ;
-if ( $tool eq "" ) {
-   die "not configured for using CTtools\n" ;
-}
-
-require "$tool/built/include/ctutils.pl" ;
-require "$tool/built/include/ctvspec.pl" ;
-require "$tool/built/include/ctquery.pl" ;
-require "$tool/built/include/ctproj.pl" ;
-require "$tool/built/include/ctcm.pl" ;
-
-$projname = &CTProj ;
-$projname =~ tr/A-Z/a-z/ ;
-$flav = &CTQueryProj( $projname ) ;
-$spec = &CTResolveSpec( $projname, $flav ) ;
-
-if ( $projname eq "" ) {
-   exit print "Not currently in any project tree\n" ;
-}
-
-$result = &CTCMIHave( $projname, $flav, $spec ) ;
-if ( $result ne "" ) {
-    @splitlist = split( /\n/, $result ) ;
-    foreach $item ( @splitlist ) {
-	print $item . "\n" ;
-    }
-}

+ 0 - 23
dtool/src/attach/ctmake

@@ -1,23 +0,0 @@
-#!/usr/bin/perl
-
-$tool = $ENV{"DTOOL"} ;
-if ( $tool eq "" ) {
-    die "not configured for using ct-tools\n" ;
-}
-
-require "$tool/built/include/ctutils.pl" ;
-require "$tool/built/include/ctvspec.pl" ;
-require "$tool/built/include/ctquery.pl" ;
-require "$tool/built/include/ctproj.pl" ;
-require "$tool/built/include/ctcm.pl" ;
-
-$projname = &CTProj ;
-$projname =~ tr/A-Z/a-z/ ;
-$flav = &CTQueryProj( $projname ) ;
-$spec = &CTResolveSpec( $projname, $flav ) ;
-
-$line = join( " ", @ARGV ) ;
-
-if ( ! &CTCMMake( $line, $projname, $spec ) ) {
-    print STDERR "Could not make '$line'\n" ;
-}

+ 0 - 55
dtool/src/attach/ctmkdir

@@ -1,55 +0,0 @@
-#!/usr/bin/perl
-
-sub CTMkDirUsage {
-    print STDERR "Usage: ctmkdir [-c \"comment\"] [-nc] dir-name [...]\n" ;
-    print STDERR "Options:\n" ;
-    print STDERR "  -c \"comment\" : provide a comment about this action\n" ;
-    print STDERR "  -nc : expect no comment on this action\n" ;
-    exit ;
-}
-
-if ( $#ARGV < 0 ) {
-    &CTMkDirUsage ;
-}
-
-$tool = $ENV{"DTOOL"} ;
-if ( $tool eq "" ) {
-   die "Environment not configured for CTtools" ;
-}
-
-require "$tool/built/include/ctutils.pl" ;
-require "$tool/built/include/ctvspec.pl" ;
-require "$tool/built/include/ctquery.pl" ;
-require "$tool/built/include/ctproj.pl" ;
-require "$tool/built/include/ctcm.pl" ;
-
-$comment = "" ;
-if ( $ARGV[0] eq "-nc" ) {
-    shift( @ARGV ) ;
-    &CTUDebug( "-nc processed\n" ) ;
-}
-if ( $ARGV[0] eq "-c" ) {
-    shift( @ARGV ) ;
-    $comment = $ARGV[0] ;
-    shift( @ARGV ) ;
-    &CTUDebug( "setting comment to '" . $comment . "'\n" ) ;
-}
-
-if ( $#ARGV < 0 ) {
-    &CTMkDirUsage ;
-}
-
-$projname = &CTProj ;
-$projname =~ tr/A-Z/a-z/ ;
-$flav = &CTQueryProj( $projname ) ;
-$spec = &CTResolveSpec( $projname, $flav ) ;
-
-foreach $item ( @ARGV ) {
-    if ( -e $item ) {
-	print STDERR "Name collision on directory '$item'\n" ;
-    } else {
-	if ( ! &CTCMMkdir( $item, $projname, $spec, $comment ) ) {
-	    print STDERR "Could name make directory '$item'\n" ;
-	}
-    }
-}

+ 0 - 78
dtool/src/attach/ctmkelem

@@ -1,78 +0,0 @@
-#!/usr/bin/perl
-
-sub CTMkElemUsage {
-    print STDERR "Usage: ctmkelem [-c \"comment\"] [-nc] [-eltype type] element-name [...]\n" ;
-    print STDERR "Options:\n" ;
-    print STDERR "  -c \"comment\" : provide a comment about this action\n" ;
-    print STDERR "  -nc : expect no comment on this action\n" ;
-    print STDERR "  -eltype type : element type\n" ;
-    exit ;
-}
-
-if ( $#ARGV < 0 ) {
-    &CTMkElemUsage ;
-}
-
-$tool = $ENV{"DTOOL"} ;
-if ( $tool eq "" ) {
-   die "Environment not configured for CTtools" ;
-}
-
-require "$tool/built/include/ctutils.pl" ;
-require "$tool/built/include/ctvspec.pl" ;
-require "$tool/built/include/ctquery.pl" ;
-require "$tool/built/include/ctproj.pl" ;
-require "$tool/built/include/ctcm.pl" ;
-
-$comment = "" ;
-$eltype = "" ;
-
-$done = 0 ;
-
-while ( ! $done ) {
-    $done = 1 ;
-    if ( $ARGV[0] eq "-nc" ) {
-	shift( @ARGV ) ;
-	&CTUDebug( "-nc processed\n" ) ;
-	$done = 0 ;
-    }
-    if ( $ARGV[0] eq "-c" ) {
-	shift( @ARGV ) ;
-	$comment = $ARGV[0] ;
-	shift( @ARGV ) ;
-	&CTUDebug( "setting comment to '" . $comment . "'\n" ) ;
-	$done = 0 ;
-    }
-    if ( $ARGV[0] eq "-eltype" ) {
-	shift( @ARGV ) ;
-	$eltype = $ARGV[0] ;
-	shift( @ARGV ) ;
-	&CTUDebug( "setting eltype to '" . $eltype . "'\n" ) ;
-	$done = 0 ;
-    }
-}
-
-if ( $#ARGV < 0 ) {
-    &CTMkElemUsage ;
-}
-
-$projname = &CTProj ;
-$projname =~ tr/A-Z/a-z/ ;
-$flav = &CTQueryProj( $projname ) ;
-$spec = &CTResolveSpec( $projname, $flav ) ;
-
-foreach $item ( @ARGV ) {
-    if ( -e $item ) {
-	if ( -d $item ) {
-	    print STDERR "Cannot mkelem on an existing directory." .
-		"  Ctmkdir it first.\n" ;
-	} else {
-	    if ( ! &CTCMMkelem( $item, $projname, $spec, $comment, $eltype )) {
-		print STDERR "Could not make a versioned element of '" .
-		    $item . "'\n" ;
-	    }
-	}
-    } else {
-	print STDERR "No such file '$item'.\n" ;
-    }
-}

+ 0 - 35
dtool/src/attach/ctmv

@@ -1,35 +0,0 @@
-#!/usr/bin/perl
-
-if ( $#ARGV != 1 ) {
-	exit print "Usage: ctmv from-element to-element\n" ;
-}
-
-$tool = $ENV{"DTOOL"} ;
-if ( $tool eq "" ) {
-   die "Environment not configured for CTtools" ;
-}
-
-require "$tool/built/include/ctutils.pl" ;
-require "$tool/built/include/ctvspec.pl" ;
-require "$tool/built/include/ctquery.pl" ;
-require "$tool/built/include/ctproj.pl" ;
-require "$tool/built/include/ctcm.pl" ;
-
-$projname = &CTProj ;
-$projname =~ tr/A-Z/a-z/ ;
-$flav = &CTQueryProj( $projname ) ;
-$spec = &CTResolveSpec( $projname, $flav ) ;
-
-$from = $ARGV[0] ;
-$to = $ARGV[1] ;
-
-if ( -e $from ) {
-    if ( -e $to ) {
-	print STDERR "'$to' already exists.\n" ;
-    } else {
-	if ( ! &CTCMMv( $from, $to, $projname, $spec ) ) {
-	}
-    }
-} else {
-    print STDERR "No such element '$from'.\n" ;
-}

+ 0 - 258
dtool/src/attach/ctntool.pl

@@ -1,258 +0,0 @@
-# given a possibly empty string, format it into a comment or -nc
-# input is in:
-# $_[0] = possible comment string
-#
-# output is:
-# string for use by neartool functions
-sub CTNtoolFormatComment {
-    local( $ret ) = "" ;
-    if ( $_[0] eq "" ) {
-	$ret = "-nc" ;
-    } else {
-        $ret = "-c \"" . $_[0] . "\"" ;
-    }
-    $ret ;
-}
-
-# make a versioned directory
-# input is in:
-# $_[0] = directory to create
-# $_[1] = curr dir
-# $_[2] = possible comment
-#
-# output:
-# return success or failure
-sub CTNtoolMkdir {
-    &CTUDebug( "in CTNtoolMkdir\n" ) ;
-    local( $ret ) = 0 ;
-    local( $dir ) = $_[0] ;
-    if ( ! ( $dir =~ /^\// )) {
-	$dir = $_[1] . "/" . $dir ;
-    }
-    local( $comment ) = &CTNtoolFormatComment( $_[2] ) ;
-    # first we have to check out the parent directory
-    local( @alist ) = split( /\//, $dir ) ;
-    pop( @alist ) ;
-    local( $parent ) = join( "/", @alist ) ;
-    &CTUDebug( "parent directory of '" . $dir . "' is '" . $parent . "'\n" ) ;
-    $ret = system( "neartool co -nc $parent\n" ) ;
-    if ( $ret == 0 ) {
-	# now make the dir
-	$ret = &CTURetCode( system( "neartool mkdir " . $comment .
-				    " $dir\n" )) ;
-    } else {
-	$ret = 0 ;
-    }
-    &CTUDebug( "out of CTNtoolMkdir\n" ) ;
-    $ret ;
-}
-
-# make a versioned element
-# input is in:
-# $_[0] = element to version
-# $_[1] = curr dir
-# $_[2] = possible comment
-# $_[3] = possible eltype
-#
-# output:
-# return success or failure
-sub CTNtoolMkelem {
-    &CTUDebug( "in CTNtoolMkelem\n" ) ;
-    local( $ret ) = 0 ;
-    local( $elem ) = $_[0] ;
-    if ( ! ( $elem =~ /^\// )) {
-	$elem = $_[1] . "/" . $elem ;
-    }
-    local( $comment ) = &CTNtoolFormatComment( $_[2] ) ;
-    local( $eltype ) = $_[3] ;
-    if ( $eltype ne "" ) {
-	$eltype = "-eltype " . $eltype ;
-    }
-    local( $line ) = "neartool mkelem " . $comment . " " . $eltype . " " .
-	$elem . "\n" ;
-    &CTUDebug( $line ) ;
-    $ret = &CTURetCode( system( $line )) ;
-    &CTUDebug( "out of CTNtoolMkelem\n" ) ;
-    $ret ;
-}
-
-# delta an element
-# input is in:
-# $_[0] = element to delta
-#
-# output:
-# return success or failure
-sub CTNtoolDelta {
-    &CTUDebug( "in CTNtoolDelta\n" ) ;
-    local( $ret ) = 0 ;
-    # as Dave points out, when working off-line, delta is the same as checkin
-    $ret = &CTURetCode( system( "neartool ci " . $_[0] )) ;
-    &CTUDebug( "out of CTNtoolDelta\n" ) ;
-    $ret ;
-}
-
-# checkout an element
-# input is in:
-# $_[0] = element to checkout
-# $_[1] = possible comment
-#
-# output:
-# return success or failure
-sub CTNtoolCheckout {
-    &CTUDebug( "in CTNtoolCheckout\n" ) ;
-    local( $ret ) = 0 ;
-    local( $comment ) = &CTNtoolFormatComment( $_[1] ) ;
-    if ( ! -d $_[0] ) {
-	$ret = &CTURetCode( system( "neartool co " . $comment . " " .
-				    $_[0] )) ;
-    } else {
-	# neartool doesn't do anything about checking out directories
-	$ret = 1 ;
-    }
-    &CTUDebug( "out of CTNtoolCheckout\n" ) ;
-    $ret ;
-}
-
-# checkin an element
-# input is in:
-# $_[0] = element to checkin
-# $_[1] = possible comment
-#
-# output:
-# return success or failure
-sub CTNtoolCheckin {
-    &CTUDebug( "in CTNtoolCheckin\n" ) ;
-    local( $ret ) = 0 ;
-    local( $comment ) = &CTNtoolFormatComment( $_[1] ) ;
-    $ret = &CTURetCode( system( "neartool ci " . $comment . " " . $_[0] )) ;
-    &CTUDebug( "out of CTNtoolCheckin\n" ) ;
-    $ret ;
-}
-
-# uncheckout an element
-# input is in:
-# $_[0] = element to uncheckout
-#
-# output:
-# return success or failure
-sub CTNtoolUncheckout {
-    &CTUDebug( "in CTNtoolUncheckout\n" ) ;
-    local( $ret ) = 0 ;
-    $ret = &CTURetCode( system( "neartool unco " . $_[0] )) ;
-    &CTUDebug( "out of CTNtoolUncheckout\n" ) ;
-    $ret ;
-}
-
-# figure out what all I have checked out
-# input is in:
-# $_[0] = project
-# $_[1] = flavor
-# $_[2] = spec line
-#
-# output:
-# return a \n serperated list of elements checked out
-sub CTNtoolIHave {
-    &CTUDebug( "in CTNtoolIHave\n" ) ;
-    local( $ret ) = "" ;
-    local( $root ) = &CTProjRoot( $_[0] ) ;
-    local( *OUTPUT ) ;
-    open( OUTPUT, "neartool find " . $root . " |" ) ;
-    while ( <OUTPUT> ) {
-	$ret = $ret . $_ ;
-    }
-    close( OUTPUT ) ;
-    &CTUDebug( "out of CTNToolIHave\n" ) ;
-    $ret ;
-}
-
-# remove a versioned element
-# input is in:
-# $_[0] = element to remove
-# $_[1] = curr dir
-#
-# output:
-# return success or failure
-sub CTNtoolRmElem {
-    &CTUDebug( "in CTNtoolRmElem\n" ) ;
-    local( $ret ) = 0 ;
-    local( $elem ) = $_[0] ;
-    if ( ! ( $elem =~ /^\// )) {
-	$elem = $_[1] . "/" . $elem ;
-    }
-    # first we have to check out the parent directory
-    local( @alist ) = split( /\//, $elem ) ;
-    pop( @alist ) ;
-    local( $parent ) = join( "/", @alist ) ;
-    &CTUDebug( "parent directory of '" . $elem . "' is '" . $parent . "'\n" ) ;
-    $ret = system( "neartool co -nc $parent\n" ) ;
-    if ( $ret == 0 ) {
-	# now nuke the element
-	$ret = &CTURetCode( system( "neartool rmname $elem\n" )) ;
-    } else {
-	$ret = 0 ;
-    }
-    &CTUDebug( "out of CTNtoolRmElem\n" ) ;
-    $ret ;
-}
-
-# mv a versioned element from one name to another
-# input is in:
-# $_[0] = from element
-# $_[1] = to element
-# $_[2] = current directory
-#
-# output:
-# return success or failure
-sub CTNtoolMv {
-    &CTUDebug( "in CTNtoolMv\n" ) ;
-    local( $ret ) = 0 ;
-    local( $elem ) = $_[0] ;
-    if ( ! ( $elem =~ /^\// )) {
-	$elem = $_[2] . "/" . $elem ;
-    }
-    # first we have to check out the parent directory
-    local( @alist ) = split( /\//, $elem ) ;
-    pop( @alist ) ;
-    local( $parent ) = join( "/", @alist ) ;
-    &CTUDebug( "parent directory of '" . $elem . "' is '" . $parent . "'\n" ) ;
-    local( $elem2 ) = $_[1] ;
-    if ( ! ( $elem2 =~ /^\// )) {
-	$elem2 = $_[2] . "/" . $elem2 ;
-    }
-    @alist = split( /\//, $elem2 ) ;
-    pop( @alist ) ;
-    local( $parent2 ) = join( "/", @alist ) ;
-    &CTUDebug( "parent directory of '" . $elem2 . "' is '" . $parent2 .
-	       "'\n" ) ;
-    $ret = system( "neartool co -nc $parent\n" ) ;
-    if ( $ret == 0 ) {
-	$ret = system( "neartool co -nc $parent2\n" ) ;
-	if ( $ret == 0 ) {
-	    # now move the element
-	    $ret = &CTURetCode( system( "neartool mv $elem $elem2\n" )) ;
-	} else {
-	    $ret = 0 ;
-	}
-    } else {
-	$ret = 0 ;
-    }
-    &CTUDebug( "out of CTNtoolMv\n" ) ;
-    $ret ;
-}
-
-# build a list of targets
-# input is in:
-# $_[0] = targets
-#
-# output:
-# return success or failure
-sub CTNtoolMake {
-    &CTUDebug( "in CTNtoolMake\n" ) ;
-    local( $ret ) = 0 ;
-    local( $line ) = "make " . $_[0] . "\n" ;
-    $ret = &CTURetCode( system( $line )) ;
-    &CTUDebug( "out of CTNtoolMake\n" ) ;
-    $ret ;
-}
-
-1;

+ 0 - 60
dtool/src/attach/ctproj.pl

@@ -1,60 +0,0 @@
-require "$tool/built/include/ctutils.pl" ;
-
-# return the root of the given project.
-sub CTProjRoot {
-    local( $CTPRtmp ) = $_[0] ;
-    $CTPRtmp =~ tr/a-z/A-Z/ ;
-    local( $CTPRret ) = $ENV{ $CTPRtmp } ;
-    $CTPRret ;
-}
-
-# return the package we're currently in.
-# input:
-#   $_[0] = project
-sub CTProjPkg {
-    local( $CTPPret ) = &CTUCurrDir() ;
-    local( $CTPPtmp ) = $_[0] ;
-    $CTPPtmp  =~ tr/a-z/A-Z/ ;
-    $CTPPret =~ s/$ENV{ $CTPPtmp }// ;
-    $CTPPret =~ s/\/src\/// ;
-    $CTPPret =~ s/\/metalibs\/// ;
-    $CTPPret ;
-}
-
-# reutrn the project containing the given directory.  If no directory is given,
-# return the project containing the current directory.
-sub CTProj {
-   local( $CTPdir ) ;
-   if ($_[0] eq "") {
-      $CTPdir = &CTUCurrDir() ;
-   } else {
-      # provided directory
-      $CTPdir = $_[0] ;
-   }
-   local( $CTPprojs ) = $ENV{"CTPROJS"} ;
-   local( $CTPdone ) = "" ;
-   local( @CTPlist ) ;
-   @CTPlist = split( / /, $CTPprojs ) ;
-   local( @CTPlist2 ) ;
-   local( $CTPtry ) ;
-   while (( $CTPdone eq "" ) && ( @CTPlist != () )){
-      # pop the first one off the list
-      $CTPtmp = $CTPlist[0] ;
-      shift( @CTPlist ) ;
-      # split the project from it's flavor
-      @CTPlist2 = split( /:/, $CTPtmp );
-      $CTPtry = &CTProjRoot( $CTPlist2[0] ) ;
-      # is CTPtry prefix of CTPdir?  if so we have our winner
-      if ( $CTPdir =~ /^$CTPtry/ ) {
-	 $CTPdone = "yep" ;
-      }
-   }
-   if ( $CTPdone eq "" ) {
-      $CTPtry = "" ;
-   } else {
-      $CTPtry = $CTPlist2[0] ;
-   }
-   $CTPtry ;
-}
-
-1;

+ 0 - 42
dtool/src/attach/ctquery

@@ -1,42 +0,0 @@
-#!/usr/bin/perl
-
-# acceptable forms:
-#    ctquery            - list all attached projects and flavors
-#    ctquery project    - list the attached flavor of the named project
-#    ctquery - flavor   - list all attached projects who are attached with a
-#                         given flavor
-
-$projs = $ENV{"CTPROJS"} ;
-@projlist = split( / +/, $projs ) ;
-
-if ( $#ARGV == -1 ) {
-   # list all projects and flavors
-   print "Currently attached projects (and flavors):\n" ;
-   foreach $pair ( @projlist ) {
-      @pairlist = split( /:/, $pair ) ;
-      ( $pairtmp = $pairlist[0] ) =~ tr/A-Z/a-z/ ;
-      print "   $pairtmp ($pairlist[1])\n" ;
-   }
-} elsif (( $#ARGV == 0 ) && !($ARGV[0] =~ /^\-/)) {
-   # list the attached flavor of the named project
-   foreach $pair ( @projlist ) {
-      @pairlist = split( /:/, $pair ) ;
-      ( $pairtmp = $pairlist[0] ) =~ tr/A-Z/a-z/ ;
-      if ( $pairtmp eq $ARGV[0] ) {
-	 print "$pairlist[1]\n" ;
-      }
-   }
-} elsif (( $#ARGV == 1 ) && ( $ARGV[0] eq "-" )){
-   # list all attached projects who are attached with a given flavor
-   foreach $pair ( @projlist ) {
-      @pairlist = split( /:/, $pair ) ;
-      if ( $pairlist[1] eq $ARGV[1] ) {
-	 $pairlist[0] =~ tr/A-Z/a-z/ ;
-	 print "$pairlist[0]\n" ;
-      }
-   }
-} else {
-   print "Usage: ctquery [project]  -or-\n" ;
-   print "       ctquery - flavor\n" ;
-   exit ;
-}

+ 0 - 37
dtool/src/attach/ctquery.pl

@@ -1,37 +0,0 @@
-# return the attached flavor of given project (or empty string)
-sub CTQueryProj {
-   local( $projs ) = $ENV{"CTPROJS"} ;
-   local( @projlist ) ;
-   @projlist = split( / +/, $projs ) ;
-   local( $pair ) ;
-   local( @pairlist ) ;
-   local( $ret ) = "" ;
-   foreach $pair ( @projlist ) {
-      @pairlist = split( /:/, $pair ) ;
-      $pairlist[0] =~ tr/A-Z/a-z/ ;
-      if ( $pairlist[0] eq $_[0] ) {
-	 $ret = $pairlist[1] ;
-      }
-   }
-   $ret ;
-}
-
-# return all projects attached with a given flavor
-sub CTQueryFlav {
-   local( $projs ) = $ENV{"CTPROJS"} ;
-   local( @projlist ) ;
-   @projlist = split( / +/, $projs ) ;
-   local( $pair ) ;
-   local( @pairlist ) ;
-   local( $ret ) = "" ;
-   foreach $pair ( @projlist ) {
-      @pairlist = split( /:/, $pair ) ;
-      if ( $pairlist[1] eq $_[0] ) {
-	 $pairlist[0] =~ tr/A-Z/a-z/ ;
-	 $ret = $ret . " $pairlist[0]" ;
-      }
-   }
-   $ret ;
-}
-
-1;

+ 0 - 31
dtool/src/attach/ctrm

@@ -1,31 +0,0 @@
-#!/usr/bin/perl
-
-if ( $#ARGV < 0 ) {
-	exit print "Usage: ctrmelem element-name [...]\n" ;
-}
-
-$tool = $ENV{"DTOOL"} ;
-if ( $tool eq "" ) {
-   die "Environment not configured for CTtools" ;
-}
-
-require "$tool/built/include/ctutils.pl" ;
-require "$tool/built/include/ctvspec.pl" ;
-require "$tool/built/include/ctquery.pl" ;
-require "$tool/built/include/ctproj.pl" ;
-require "$tool/built/include/ctcm.pl" ;
-
-$projname = &CTProj ;
-$projname =~ tr/A-Z/a-z/ ;
-$flav = &CTQueryProj( $projname ) ;
-$spec = &CTResolveSpec( $projname, $flav ) ;
-
-foreach $item ( @ARGV ) {
-    if ( -e $item ) {
-	if ( ! &CTCMRmElem( $item, $projname, $spec ) ) {
-	    print STDERR "Could not rmname '$item'\n" ;
-	}
-    } else {
-	print STDERR "No such file '$item'.\n" ;
-    }
-}

+ 0 - 43
dtool/src/attach/ctsanity

@@ -1,43 +0,0 @@
-#!/usr/bin/perl
-
-$tool = $ENV{"DTOOL"} ;
-if ( $tool eq "" ) {
-    die "Environment not configured to run CTtools" ;
-}
-
-sub CTSanityUsage {
-    print STDERR "Usage: ctsanity [-v]\n" ;
-    print STDERR "Options:\n" ;
-    print STDERR "  -v : sanity check the .vspec files \n" ;
-    exit ;
-}
-
-if ( $#ARGV == -1 ) {
-    &CTSanityUsage ;
-}
-
-$check_vspecs = 0 ;
-
-foreach $item ( @ARGV ) {
-    if ( $item eq "-v" ) {
-	$check_vspecs = 1 ;
-    } else {
-	print STDERR "unknown option '" . $item . "'\n" ;
-	$CTSanityUsage ;
-    }
-}
-
-require "$tool/built/include/ctvspec.pl" ;
-
-if ( $check_vspecs ) {
-    local( $projs ) = &CTListAllProjects ;
-    local( @projlist ) = split( / +/, $projs ) ;
-    local( $item ) ;
-    foreach $item ( @projlist ) {
-	print STDERR "checking " . $item . ".vspec:\n" ;
-	local( $ctsavedebug ) = $ctdebug ;
-	$ctdebug = 1 ;
-	&CTReadVSpec( $item ) ;
-	$ctdebug = $ctsavedebug ;
-    }
-}

+ 0 - 43
dtool/src/attach/cttimewarp

@@ -1,43 +0,0 @@
-#!/usr/bin/perl
-
-if ($#ARGV < 0) {
-    exit print "Usage: cttimewarp [-clear] label [time]\n" ;
-}
-
-@arglist = @ARGV ;
-
-$clear = 0 ;
-
-if ( $arglist[0] =~ /^-c/ ) {
-    $clear = 1 ;
-    shift( @arglist ) ;
-}
-
-if ( @arglist == () ) {
-    if ( $clear ) {
-	exit print "Usage: cttimewarp -clear label\n" ;
-    } else {
-	exit print "Usage: cttimewarp label time\n" ;
-    }
-}
-
-$label = $arglist[0] ;
-shift( @arglist ) ;
-
-if (( ! $clear ) && ( @arglist == () )) {
-    exit print "Usage: cttimewarp label time\n" ;
-}
-
-$time = $arglist[0] ;
-
-if ( $clear ) {
-    $cmd = "cleartool find . -version \"lbtype(" . $label .
-           ")\" -exec 'cleartool rmlabel -c \"untimewarping\" " . $label .
-           ' $CLEARCASE_XPN' . "'\n" ;
-    system( $cmd ) ;
-} else {
-    $cmd = "cleartool mklabel -replace -recurse -c \"rolling time back to " .
-           $time . "\" -version /main/'{\!created_since(" . $time . ")}' " .
-           $label . " .\n" ;
-    system( $cmd ) ;
-}

+ 0 - 49
dtool/src/attach/ctunattach.drv

@@ -1,49 +0,0 @@
-#!/usr/bin/perl
-
-# acceptable forms:
-#   ctunattach project      - attach to the personal flavor of the project
-
-sub CTUnattachUsage {
-   print STDERR "Usage: ctattach project(s)\n" ;
-   &CTAttachWriteNullScript( $tmpname ) ;
-   print $tmpname . "\n" ;
-   exit;
-}
-
-$tool = $ENV{"DTOOL"} ;
-
-require "$tool/built/include/ctattch.pl" ;
-require "$tool/built/include/ctunattach.pl" ;
-require "$tool/built/include/ctquery.pl" ;
-
-$tmpname = "/tmp/script.$$" ;
-
-if ( $#ARGV == -1 ) {
-   &CTUnattachUsage ;
-}
-
-foreach $proj ( @ARGV ) {
-   &CTUDebug( "project is '$proj'\n" ) ;
-
-   $curflav = &CTQueryProj( $proj ) ;
-   if ( $curflav ne "" ) {
-      $envsep{"PATH"} = ":" ;
-      $envsep{"LD_LIBRARY_PATH"} = ":" ;
-      $envsep{"DYLD_LIBRARY_PATH"} = ":" ;
-      $envsep{"PFPATH"} = ":" ;
-      $envsep{"SSPATH"} = ":" ;
-      $envsep{"STKPATH"} = ":" ;
-      $envsep{"DC_PATH"} = ":" ;
-      $spec = &CTUnattachCompute( $proj, $curflav ) ;
-      if ( $spec eq "" ) {
-	 &CTAttachWriteNullScript( $tmpname ) ;
-	 print $tmpname . "\n" ;
-      } else {
-	 &CTUnattachWriteScript( $tmpname ) ;
-	 print $tmpname . "\n" ;
-      }
-   } else {
-      &CTAttachWriteNullScript( $tmpname ) ;
-      print $tmpname . "\n" ;
-   }
-}

+ 0 - 251
dtool/src/attach/ctunattach.pl

@@ -1,251 +0,0 @@
-require "$tool/built/include/ctquery.pl" ;
-
-$shell_type = "csh" ;
-if ( $ENV{"SHELL_TYPE"} ne "" ) {
-    if ( $ENV{"SHELL_TYPE"} eq "sh" ) {
-	$shell_type = "sh" ;
-    }
-}
-
-# remove a value from a variable.  If it is the only thing remaining in the
-# variable, add it to the unset list.
-# input is in:
-# $_[0] = variable
-# $_[1] = value
-#
-# output is in:
-# %newenv = an image of how we want the environment to be
-# @unset = a list of variables to unset
-sub CTUnattachMod {
-    &CTUDebug( "in CTUnattachMod\n" ) ;
-    local( $done ) = 0 ;
-    # if we didn't get any data, nothing really to do
-    if ( $_[0] eq "" ) { $done = 1 ; }
-    if ( $_[1] eq "" ) { $done = 1 ; }
-    # if the variable is already set to be unset, nothing really to do
-    if ( join( " ", @unset ) =~ /$_[0]/ ) { $done = 1 ; }
-    # if the variable isn't in newenv, move it there, if it's empty mark it
-    # for unsetting
-    if ( $newenv{$_[0]} eq "" ) {
-	$newenv{$_[0]} = &CTSpoolEnv( $_[0] ) ;
-	if ( $newenv{$_[0]} eq "" ) {
-	    push( @unset, $_[0] ) ;
-	    delete $newenv{$_[0]} ;
-	    $done = 1 ;
-	}
-    }
-    # if the value does not appear in the variable, nothing really to do
-    if ( ! ( $newenv{$_[0]} =~ /$_[1]/ ) ) { $done = 1 ; }
-    # now down to the real work
-    if ( ! $done ) {
-	# if the variable is exactly the value, mark it for unsetting
-	if ( $newenv{$_[0]} eq $_[1] ) {
-	    push( @unset, $_[0] ) ;
-	    delete $newenv{$_[0]} ;
-	} elsif ( $newenv{$_[0]} =~ / $_[1]/ ) {
-	    local( $tmp ) = $newenv{$_[0]} ;
-	    $tmp =~ s/ $_[1]// ;
-	    $newenv{$_[0]} = $tmp ;
-	} elsif ( $newenv{$_[0]} =~ /$_[1] / ) {
-	    local( $tmp ) = $newenv{$_[0]} ;
-	    $tmp =~ s/$_[1] // ;
-	    $newenv{$_[0]} = $tmp ;
-	} else {
-	    print STDERR "ERROR: variable '" . $_[0] . "' contains '" .
-		$_[1] . "' (in '" . $newenv{$_[0]} .
-	        "'), but I am too stupid to figure out how to remove it.\n" ;
-	}
-    }
-}
-
-# given the project and flavor, build the lists of variables to set/modify
-# input is in:
-# $_[0] = project
-# $_[1] = flavor
-#
-# output is in:
-# return value is config line
-# %newenv      = an image of what we want the environment to look like
-# @unset       = list of variables to be unset
-# %envsep      = seperator
-# %envcmd      = set or setenv
-# %envpostpend = flag that variable should be postpended
-sub CTUnattachCompute {
-   &CTUDebug( "in CTUnattachCompute\n" ) ;
-   local( $flav ) = $_[1] ;
-   local( $spec ) = &CTResolveSpec( $_[0], $flav ) ;
-   local( $root ) = &CTComputeRoot( $_[0], $flav, $spec ) ;
-
-   if ( $spec ne "" ) {
-      local( $proj ) = $_[0] ;
-      $proj =~ tr/a-z/A-Z/ ;
-      local( $item ) ;
-
-      # since we don't have to worry about sub-attaches, it doesn't matter
-      # if we scan the .init file first or not.  So we won't.
-      &CTUDebug( "extending paths\n" ) ;
-
-      $item = $root . "/built/bin" ;
-      &CTUnattachMod( "PATH", $item ) ;
-      $item = $root . "/built/lib" ;
-      if ( $ENV{"PENV"} eq "WIN32" ) {
-	  &CTUnattachMod( "PATH", $item ) ;
-      }
-      &CTUnattachMod( "LD_LIBRARY_PATH", $item ) ;
-      &CTUnattachMod( "DYLD_LIBRARY_PATH", $item ) ;
-      #$item = $root . "/src/all" ;
-      #&CTUnattachMod( "CDPATH", $item ) ;
-      $item = $root . "/built/include" ;
-      &CTUnattachMod( "CT_INCLUDE_PATH", $item ) ;
-      $item = $root . "/built/etc" ;
-      &CTUnattachMod( "ETC_PATH", $item ) ;
-      $item = $proj . ":" . $flav ;
-      &CTUnattachMod( "CTPROJS", $item ) ;
-      push( @unset, $proj ) ;
-
-      if ( -e "$root/built/etc/$_[0].init" ) {
-	 &CTUDebug( "scanning $_[0].init file\n" ) ;
-	 local( @linesplit ) ;
-	 local( $linetmp ) ;
-	 local( $loop );
-	 local( *INITFILE ) ;
-	 if ( -x "$root/built/etc/$_[0].init" ) {
-	    open( INITFILE, "$root/built/etc/$_[0].init $_[0] $_[1] $root |" ) ;
-	 } else {
-	    open( INITFILE, "< $root/built/etc/$_[0].init" ) ;
-	 }
-	 while ( <INITFILE> ) {
-	    s/\n$// ;
-	    @linesplit = split( /\#/ ) ;
-	    $_ = $linesplit[0] ;
-	    if ( $_ =~ /^MODABS/ ) {
-	       @linesplit = split ;
-	       $linetmp = $linesplit[1] ;
-	       shift( @linesplit ) ;
-	       shift( @linesplit ) ;
-	       foreach $loop ( @linesplit ) {
-		   &CTUnattachMod( $linetmp, $loop ) ;
-	       }
-	    } elsif ( $_ =~ /^MODREL/ ) {
-	       @linesplit = split ;
-	       $linetmp = $linesplit[1] ;
-	       shift( @linesplit ) ;
-	       shift( @linesplit ) ;
-	       foreach $loop ( @linesplit ) {
-		   &CTUnattachMod( $linetmp, $root . "/" . $loop ) ;
-	       }
-	    } elsif ( $_ =~ /^SETABS/ ) {
-	       @linesplit = split ;
-	       $linetmp = $linesplit[1] ;
-	       push( @unset, $linetmp ) ;
-	    } elsif ( $_ =~ /^SETREL/ ) {
-	       @linesplit = split ;
-	       $linetmp = $linesplit[1] ;
-	       push( @unset, $linetmp ) ;
-	    } elsif ( $_ =~ /^SEP/ ) {
-	       @linesplit = split ;
-	       $envsep{$linesplit[1]} = $linesplit[2] ;
-	    } elsif ( $_ =~ /^CMD/ ) {
-	       @linesplit = split ;
-	       $envcmd{$linesplit[1]} = $linesplit[2] ;
-	    } elsif ( $_ =~ /^DOCSH/ ) {
-	       &CTUDebug( "ignoring DO command in .init file\n" ) ;
-	    } elsif ( $_ =~ /^DOSH/ ) {
-	       &CTUDebug( "ignoring DO command in .init file\n" ) ;
-	    } elsif ( $_ =~ /^DO/ ) {
-	       &CTUDebug( "ignoring DO command in .init file\n" ) ;
-	    } elsif ( $_ =~ /^POSTPEND/ ) {
-	       @linesplit = split ;
-	       $envpospend{$linesplit[1]} = 1 ;
-	    } elsif ( $_ =~ /^ATTACH/ ) {
-	       &CTUDebug( "ignoring ATTACH command in .init file\n" ) ;
-	    } else {
-	       print STDERR "Unknown .init directive '$_'\n" ;
-	    }
-	 }
-	 close( INITFILE ) ;
-      }
-   }
-   &CTUDebug( "out of CTUnattachCompute\n" ) ;
-   $spec ;
-}
-
-# write a script to setup the environment
-# Input is:
-# $_[0] = filename
-sub CTUnattachWriteScript {
-   &CTUDebug( "in CTAttachWriteScript\n" ) ;
-   local( *OUTFILE ) ;
-   open( OUTFILE, ">$_[0]" ) ;
-   print OUTFILE "#!/bin/" . $shell_type . " -f\n" ;
-   local( $item ) ;
-   #local( $unsetcdpath ) = 0 ;
-   #local( $modcdpath ) = 0 ;
-
-   foreach $item ( @unset ) {
-       #if ( $item eq "CDPATH" ) { $unsetcdpath = 1 ; }
-
-       if ( $shell_type eq "sh" ) {
-	   print OUTFILE "$item=\n" ;
-	   if ( $envcmd{$item} ne "set" ) {
-	       print OUTFILE "export $item\n" ;
-	   }
-       } else {
-	   if ( $envcmd{$item} ne "" ) {
-	       print OUTFILE "un" . $envcmd{$item} . " $item\n" ;
-	   } else {
-	       print OUTFILE "unsetenv $item\n" ;
-	   }
-       }
-   }
-   foreach $item ( keys %newenv ) {
-       #if ( $item eq "CDPATH" ) { $modcdpath = 1 ; }
-
-       local( $sep ) = " " ;
-       if ( $envsep{$item} ne "" ) {
-	   $sep = $envsep{$item} ;
-       }
-       local( @splitlist ) = split( / +/, $newenv{$item} ) ;
-       local( $outval ) = join( $sep, @splitlist ) ;
-
-       if ( $shell_type eq "sh" ) {
-	   print OUTFILE "$item=\"" . $outval . "\"\n" ;
-	   if ( $envcmd{$item} ne "set" ) {
-	       print OUTFILE "export $item\n" ;
-	   }
-       } else {
-	   if ( $envcmd{$item} ne "" ) {
-	       PRINT OUTFILE $envcmd{$item} . " $item " ;
-	       if ( $envcmd{$item} eq "set" ) {
-		   print OUTFILE " = ( " ;
-	       }
-	       print OUTFILE $outval ;
-	       if ( $envcmd{$item} eq "set" ) {
-		   print OUTFILE ")" ;
-	       }
-	       print OUTFILE "\n" ;
-	   } else {
-	       print OUTFILE "setenv $item \"$outval\"\n" ;
-	   }
-       }
-   }
-   #if ( $unsetcdpath ) {
-   #    if ( $shell_type ne "sh" ) {
-   #	   print OUTFILE "unset cdpath\n" ;
-   #    }
-   #} elsif ( $modcdpath ) {
-   #    if ( $shell_type ne "sh" ) {
-   #	   print OUTFILE "set cdpath = ( \$" . "CDPATH )\n" ;
-   #    }
-   #}
-
-   if (! $ctdebug) {
-      print OUTFILE "rm -f $_[0]\n" ;
-   } else {
-      print STDERR "no self-destruct script '" . $_[0] . "'\n" ;
-   }
-   close( OUTFILE ) ;
-   &CTUDebug( "out of CTUnattachWriteScript\n" ) ;
-}
-
-1;

+ 0 - 31
dtool/src/attach/ctunco

@@ -1,31 +0,0 @@
-#!/usr/bin/perl
-
-if ( $#ARGV < 0 ) {
-    exit print "Usage ctunco element-name [...]\n" ;
-}
-
-$tool = $ENV{"DTOOL"} ;
-if ( $tool eq "" ) {
-    die "not configured for using CTtools" ;
-}
-
-require "$tool/built/include/ctutils.pl" ;
-require "$tool/built/include/ctvspec.pl" ;
-require "$tool/built/include/ctquery.pl" ;
-require "$tool/built/include/ctproj.pl" ;
-require "$tool/built/include/ctcm.pl" ;
-
-$projname = &CTProj ;
-$projname =~ tr/A-Z/a-z/ ;
-$flav = &CTQueryProj( $projname ) ;
-$spec = &CTResolveSpec( $projname, $flav ) ;
-
-foreach $item ( @ARGV ) {
-    if ( -e $item ) {
-	if ( ! &CTCMUncheckout( $item, $projname, $spec ) ) {
-	    print STDERR "Could not uncheckout '$item'\n" ;
-	}
-    } else {
-	print STDERR "No such file '$item'.\n" ;
-    }
-}

+ 0 - 47
dtool/src/attach/ctutils.pl

@@ -1,47 +0,0 @@
-# evaluate the given parameter to expand shell variables
-sub CTUShellEval {
-   local( *CTUSEFILE ) ;
-   open( CTUSEFILE, "echo $_[0] |" ) ;
-   local( $CTUSEret ) = <CTUSEFILE> ;
-   close( CTUSEFILE ) ;
-   $CTUSEret =~ s/\n$// ;
-   $CTUSEret ;
-}
-
-# if debug is on, print the argument
-sub CTUDebug {
-    if ( $ctdebug ) {
-	print STDERR $_[0] ;
-    }
-}
-
-use Cwd ;
-# get current directory
-sub CTUCurrDir {
-    local( $pwd ) = getcwd() ;
-    if ( $pwd =~ /^\/vobs/ ) {
-	local( *VFILE ) ;
-	open( VFILE, "cleartool pwv -short |" ) ;
-	local( $view ) = <VFILE> ;
-	close( VFILE ) ;
-	$view =~ s/\n$// ;
-	$pwd = "/view/" . $view . $pwd ;
-    }
-    $pwd ;
-}
-
-# turn a shell return code into a success/fail flag
-sub CTURetCode {
-    local( $ret ) ;
-    if ( $_[0] == 0 ) {
-	$ret = 1 ;
-    } else {
-	$ret = 0 ;
-    }
-    $ret ;
-}
-
-$ctdebug = $ENV{"CTATTACH_DEBUG"} ;
-$ctvspec_path = '/usr/local/etc' unless $ctvspec_path = $ENV{'CTVSPEC_PATH'};
-
-1;

+ 0 - 360
dtool/src/attach/ctvspec.pl

@@ -1,360 +0,0 @@
-require "$tool/built/include/ctutils.pl" ;
-
-# read a .vspec file into a map
-# $_[0] = project
-# on exit $ctvspecs{} will contain the data
-#
-# vspec format:
-#   tag:type:other data
-#
-#   type: ref, root, vroot, croot
-#   other data:
-#     ref: name=_____ - required, take to refference
-#     root: path=_____ - required, path of tree root
-#     vroot: name=_____ - optional, name of view to use (if not tag)
-#     croot: path=_____ - required, local path of tree root
-#            server=_____ - required, CVS server string, ',' for ':'
-
-sub CTReadVSpec {
-    &CTUDebug( "reading vspec file for project " . $_[0] . "\n" ) ;
-    local( $ret ) = "" ;
-    local( $thisproj ) = $_[0] ;
-    if ( -e "$ctvspec_path/$thisproj.vspec" ) {
-        %ctvspecs = () ;
-        local( *SPECFILE ) ;
-        open( SPECFILE, "<$ctvspec_path/$thisproj.vspec" ) ;
-        local( @partlist ) ;
-        while ( $_ = <SPECFILE> ) {
-            s/\n$// ;
-            @partlist = split( /\#/ ) ;
-            $_ = $partlist[0] ;
-            if ( $_ ne "" ) {
-                @partlist = split( /:/ );
-                local( $tag ) = $partlist[0] ;
-                shift( @partlist ) ;
-                local( $spec ) = join( ":", @partlist ) ;
-                if ( &CTValidateSpec( $spec ) ) {
-                    $ctvspecs{$tag} = $spec ;
-                    if ( $ctdebug ) {
-                        print STDERR "tag(" . $tag . ") = " . $spec . "\n" ;
-                    }
-                }
-            }
-        }
-        close( SPECFILE ) ;
-        $ctvspec_read = $_[0] ;
-    } else {
-        print STDERR "CTReadVSpec: cannot locate '$ctvspec_path/$thisproj.vspec'\n" ;
-        print STDERR "(did you forget to run the \$WINTOOLS/cp_vspec script?)\n" ;
-    }
-}
-
-# given a spec line return it's type
-# $_[0] = spec line
-
-sub CTSpecType {
-    local( @speclist ) = split( /:/, $_[0] ) ;
-    $speclist[0] ;
-}
-
-# given a spec line return it's options if any
-# $_[0] = spec line
-
-sub CTSpecOptions {
-    local( @speclist ) = split( /:/, $_[0] ) ;
-    shift( @speclist ) ;
-    join( ":", @speclist ) ;
-}
-
-# given the options part of a spec line, find a given option
-# $_[0] = options line
-# $_[1] = desired option
-
-sub CTSpecFindOption {
-    local( $ret ) = "" ;
-    local( @options ) = split( /:/, $_[0] ) ;
-    local( $item ) ;
-    local( @itemlist ) ;
-    foreach $item ( @options ) {
-        @itemlist = split( /=/, $item ) ;
-        if ( $itemlist[0] eq $_[1] ) {
-            $ret = $itemlist[1] ;
-        }
-    }
-    $ret ;
-}
-
-# resolve a final spec line for a given flavor
-# $_[0] = project
-# $_[1] = flavor
-
-sub CTResolveSpec {
-    &CTUDebug( "in CTResolveSpec\n" ) ;
-    local( $proj ) = $_[0] ;
-    $proj =~ tr/A-Z/a-z/ ;
-    if ( $ctvspec_read ne $proj ) {
-        &CTReadVSpec( $proj ) ;
-    }
-    local( $spec ) = $ctvspecs{$_[1]} ;
-    local( $ret ) = "" ;
-    if ( $spec ne "" ) {
-        local( $type ) = &CTSpecType( $spec ) ;
-        local( @speclist ) = split( /:/, &CTSpecOptions( $spec ) ) ;
-        if ( $type eq "ref" ) {
-            local( @optionlist ) = split( /=/, $speclist[0] ) ;
-            if ( $optionlist[0] ne "name" ) {
-                print STDERR "bad data attached to flavor " . $_[1] .
-                    " of project " . $proj . "\n" ;
-            } else {
-                local( $tmp ) = &CTUShellEval( $optionlist[1] ) ;
-                if ( $ctdebug ) {
-                    print STDERR "resolved a 'ref' to " . $tmp .
-                        ", recuring\n" ;
-                }
-                $ret = &CTResolveSpec( $proj, $tmp ) ;
-            }
-        } else {
-            $ret = $spec ;
-        }
-    }
-    if ( $ret eq "" ) {
-        print STDERR "unknown flavor " . $_[1] . " of project " . $proj .
-            "\n" ;
-    }
-    &CTUDebug( "out of CTResolveSpec\n" ) ;
-    $ret ;
-}
-
-# resolve the final name for a given flavor
-# $_[0] = project
-# $_[1] = flavor
-
-sub CTResolveSpecName {
-    &CTUDebug( "in CTResolveSpecName\n" ) ;
-    local( $proj ) = $_[0] ;
-    $proj =~ tr/A-Z/a-z/ ;
-    if ( $ctvspec_read ne $proj ) {
-        &CTReadVSpec( $proj ) ;
-    }
-    local( $spec ) = $ctvspecs{$_[1]} ;
-    local( $ret ) = $_[1] ;
-    if ( $spec ne "" ) {
-        local( $type ) = &CTSpecType( $spec ) ;
-        local( @speclist ) = split( /:/, &CTSpecOptions( $spec ) ) ;
-        if ( $type eq "ref" ) {
-            local( @optionlist ) = split( /=/, $speclist[0] ) ;
-            if ( $optionlist[0] ne "name" ) {
-                print STDERR "bad data attached to flavor " . $_[1] .
-                    " of project " . $proj . "\n" ;
-            } else {
-                local( $tmp ) = &CTUShellEval( $optionlist[1] ) ;
-                if ( $ctdebug ) {
-                    print STDERR "resolved a 'ref' to " . $tmp .
-                        ", recuring\n" ;
-                }
-                $ret = &CTResolveSpecName( $proj, $tmp ) ;
-            }
-        }
-    }
-    if ( $ret eq "" ) {
-        print STDERR "unknown flavor " . $_[1] . " of project " . $proj .
-            "\n" ;
-    }
-    &CTUDebug( "out of CTResolveSpecName\n" ) ;
-    $ret ;
-}
-
-# validate a spec line
-# $_[0] = spec line
-
-sub CTValidateSpec {
-    local( $ret ) = 0 ;
-    local( $type ) = &CTSpecType( $_[0] ) ;
-    local( @speclist ) = split( /:/, &CTSpecOptions( $_[0] ) ) ;
-    local( $have_error ) = 0 ;
-    local( $item ) ;
-    local( @itemlist ) ;
-    if ( $type eq "ref" ) {
-        local( $have_name ) = 0 ;
-        foreach $item ( @speclist ) {
-            @itemlist = split( /=/, $item ) ;
-            if ( $itemlist[0] eq "name" ) {
-                if ( $have_name ) {
-                    $have_error = 1;
-                    &CTUDebug( "multiple name options on 'ref'\n" ) ;
-                }
-                $have_name = 1;
-            } else {
-                &CTUDebug( "invalid option on 'ref' = " . $item . "\n" ) ;
-                $have_error = 1 ;
-            }
-        }
-        if ( ! $have_error ) {
-            if ( $have_name ) {
-                $ret = 1 ;
-            }
-        }
-    } elsif ( $type eq "root" ) {
-        local( $have_path ) = 0 ;
-        foreach $item ( @speclist ) {
-            @itemlist = split( /=/, $item ) ;
-            if ( $itemlist[0] eq "path" ) {
-                if ( $have_path ) {
-                    $have_error = 1 ;
-                    &CTUDebug( "multiple path options on 'root'\n" ) ;
-                }
-                $have_path = 1 ;
-            } else {
-                &CTUDebug( "invalid option on 'root' = " . $item . "\n" ) ;
-                $have_error = 1 ;
-            }
-        }
-        if ( ! $have_error ) {
-            if ( $have_path ) {
-                $ret = 1 ;
-            }
-        }
-    } elsif ( $type eq "vroot" ) {
-        local( $have_name ) = 0 ;
-        foreach $item ( @speclist ) {
-            @itemlist = split( /=/, $item ) ;
-            if ( $itemlist[0] eq "name" ) {
-                if ( $have_name ) {
-                    $have_error = 1 ;
-                    &CTUDebug( "multiple name options on 'vroot'\n" ) ;
-                }
-                $have_name = 1 ;
-            } else {
-                &CTUDebug( "invalid option on 'vroot' = " . $item . "\n" ) ;
-                $have_error = 1 ;
-            }
-        }
-        if ( ! $have_error ) {
-            $ret = 1 ;
-        }
-    } elsif ( $type eq "croot" ) {
-        local( $have_path ) = 0 ;
-        local( $have_server ) = 0 ;
-        foreach $item ( @speclist ) {
-            @itemlist = split( /=/, $item ) ;
-            if ( $itemlist[0] eq "path" ) {
-                if ( $have_path ) {
-                    $have_error = 1 ;
-                    &CTUDebug( "multiple path options on 'croot'\n" ) ;
-                }
-                $have_path = 1 ;
-            } elsif ( $itemlist[0] eq "server" ) {
-                if ( $have_server ) {
-                    $have_error = 1 ;
-                    &CTUDebug( "multiple server options on 'croot'\n" ) ;
-                }
-                $have_server = 1 ;
-            } else {
-                &CTUDebug( "invalid option on 'croot' = " . $item . "\n" ) ;
-                $have_error = 1 ;
-            }
-        }
-        if ( ! $have_error ) {
-            if ( $have_path && $have_server ) {
-                $ret = 1 ;
-            }
-        }
-    } else {
-        &CTUDebug( "unknow spec type '" . $speclist[0] . "'\n" ) ;
-    }
-    $ret ;
-}
-
-# get a list of all projects
-
-sub CTListAllProjects {
-    &CTUDebug( "in CTListAllProjects\n" ) ;
-    local( $ret ) = "" ;
-    local( $done ) = 0 ;
-    local( *DIRFILES ) ;
-    open( DIRFILES, "(cd $ctvspec_path ; /bin/ls -1 *.vspec ; echo blahblah) |" ) ;
-    while ( ! $done ) {
-        $_ = <DIRFILES> ;
-        s/\n$// ;
-        if ( $_ eq "blahblah" ) {
-            $done = 1 ;
-        } else {
-            s/.vspec$// ;
-            if ( $_ ne "" ) {
-                if ( $ret eq "" ) {
-                    $ret = $_ ;
-                } else {
-                    $ret = $ret . " " . $_ ;
-                }
-            }
-        }
-    }
-    close( DIRFILES ) ;
-    &CTUDebug( "final list of projects '" . $ret . "'\n" .
-               "out of CTListAllProjects\n" ) ;
-    $ret ;
-}
-
-# list all flavors of a project
-# $_[0] = project
-
-sub CTListAllFlavors {
-    &CTUDebug( "in CTListAllFlavors\n" ) ;
-    local( $proj ) = $_[0] ;
-    $proj =~ tr/A-Z/a-z/ ;
-    if ( $ctvspec_read ne $proj ) {
-        &CTReadVSpec( $proj ) ;
-    }
-    local( $ret ) = "";
-    local( $item ) ;
-    foreach $item ( keys %ctvspecs ) {
-        if ( $ret eq "" ) {
-            $ret = $item ;
-        } else {
-            $ret = $ret . " " . $item ;
-        }
-    }
-    &CTUDebug( "out of CTListAllFlavors\n" ) ;
-    $ret ;
-}
-
-# given a project and a spec, determine the local root of the project
-# $_[0] = project
-# $_[1] = flavor
-# $_[2] = spec line
-
-sub CTComputeRoot {
-    &CTUDebug( "in CTComputeRoot\n" ) ;
-    local( $proj ) = $_[0] ;
-    $proj =~ tr/A-Z/a-z/ ;
-    if ( $ctvspec_read ne $proj ) {
-        &CTReadVSpec( $proj ) ;
-    }
-    local( $ret ) = "" ;
-    local( $type ) = &CTSpecType( $_[2] ) ;
-    local( $options ) = &CTSpecOptions( $_[2] ) ;
-    local( $vname ) = &CTResolveSpecName( $proj, $_[1] ) ;
-    &CTUDebug( "type = '" . $type . "' with options '" . $options . "'\n" ) ;
-    if ( $type eq "root" ) {
-        $ret = &CTSpecFindOption( $options, "path" ) ;
-    } elsif ( $type eq "vroot" ) {
-        local( $name ) = &CTSpecFindOption( $options, "name" ) ;
-        if ( $name ne "" ) {
-            $ret = "/view/$name/vobs/$proj" ;
-        } else {
-            $ret = "/view/$vname/vobs/$proj" ;
-        }
-    } elsif ( $type eq "croot" ) {
-        $ret = &CTSpecFindOption( $options, "path" ) ;
-    } elsif ( $ctdebug) {
-        print STDERR "unknown flavor type '" . $type . "'\n" ;
-    }
-    &CTUDebug( "returning '" . $ret . "'\n" ) ;
-    &CTUDebug( "out of CTComputeRoot\n" ) ;
-    $ret ;
-}
-
-%ctvspecs = () ;
-$ctvspec_read = "" ;
-
-1;

+ 0 - 37
dtool/src/attach/dtool.alias

@@ -1,37 +0,0 @@
-#!/bin/csh -f
-
-setenv OS `uname`
-setenv USER `whoami`
-
-if ( -e $DTOOL/bin/neartool ) setenv HAVE_NEARTOOL "yes"
-if ( ! $?HAVE_NEARTOOL ) setenv HAVE_NEARTOOL "no"
-if ( ! $?HAVE_ATRIA ) setenv HAVE_ATRIA "no"
-
-set host=$HOST
-if ( ! $?TERM ) setenv TERM "none"
-if ( $TERM == "iris-ansi" || $TERM == "iris-ansi-net" ) then
-  alias ctshowprojs 'echo -n "\033P1.y"$USER"@"$host" -- "$CTPROJS"\033\\"; echo -n "\033P3.y"`echo $CTPROJS | cut -f1 -d:`\($host\)"\033\\"'
-else if ( $TERM == "xterm" || $TERM == "color-xterm" || $TERM == "cygwin" ) then
-  alias ctshowprojs 'echo -n "\033]2;"$USER"@"$host" -- "$CTPROJS"\007"; echo -n "\033]1;"`echo $CTPROJS | cut -f1 -d:`\($host\)"\007"'
-else
-  alias ctshowprojs 'echo $CTPROJS'
-endif
-
-alias ctattach 'source `ctattach.drv \!*`; ctshowprojs'
-alias cta 'ctattach'
-alias cta-ship 'setenv CTSAVE $CTDEFAULT_FLAV ; setenv CTDEFAULT_FLAV ship ; ctattach \!* ; setenv CTDEFAULT_FLAV $CTSAVE ; unsetenv CTSAVE'
-alias cta-release 'setenv CTSAVE $CTDEFAULT_FLAV ; setenv CTDEFAULT_FLAV release ; ctattach \!* ; setenv CTDEFAULT_FLAV $CTSAVE ; unsetenv CTSAVE'
-alias cta-install 'setenv CTSAVE $CTDEFAULT_FLAV ; setenv CTDEFAULT_FLAV install ; ctattach \!* ; setenv CTDEFAULT_FLAV $CTSAVE ; unsetenv CTSAVE'
-alias ctunattach 'source `ctunattach.drv \!*`; ctshowprojs'
-alias ctuna 'ctunattach'
-
-#Modifications to emacs alias by Jason
-#To allow for NTEmacs to run like emacs on unix boxes
-if (($OS == "CYGWIN_NT-4.0") || ($OS == "CYGWIN_NT-5.0" ) || ($OS == "CYGWIN_NT-5.1" )) then
-  alias emacs 'emacs -T "$USER@$HOST  $CTPROJS" -xrm "Emacs*iconName: `echo $CTPROJS | cut -f1 -d:`($HOST)" -bg #002040 -fg #00C0FF -cr yellow -ms yellow -l `cygpath -w ~/.emacs` $CTEMACS_OPTS'
-else
-  alias emacs 'emacs -T "$USER@$HOST  $CTPROJS" -xrm "Emacs*iconName: `echo $CTPROJS | cut -f1 -d:`($HOST)" $CTEMACS_OPTS'
-endif
-
-alias rlogin 'rlogin \!*; ctshowprojs'
-alias telnet 'telnet \!*; ctshowprojs'

Some files were not shown because too many files changed in this diff