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
 ```
 
+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
 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-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
 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.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."+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.LightDict[\'" + light.getName() + "\']=alight\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.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."+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()+ ".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")
@@ -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.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."+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()+ ".setPos(Point3(" + str(light.getX()) + "," + str(light.getY()) + "," + str(light.getZ()) + "))\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.setExponent(" +str(light.getExponent()) +")\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()+ ".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")

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

@@ -63,13 +63,8 @@ class seLight(NodePath):
         self.lence = lence
         self.active = True
 
-        if isinstance(light, Spotlight):
-            node = light.upcastToLensNode()
-        else:
-            node = light.upcastToPandaNode()
-
         # Attach node to self
-        self.LightNode=parent.attachNewNode(node)
+        self.LightNode=parent.attachNewNode(light)
         self.LightNode.setTag("Metadata",tag)
         if(self.type=='spot'):
             self.LightNode.setHpr(self.orientation)
@@ -418,8 +413,6 @@ class seLightManager(NodePath):
         #################################################################
         type = lower(light.getType().getName())
 
-        light.upcastToNamable()
-
         specularColor = VBase4(1)
         position = Point3(0,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.
         joint = bundle.findChild(jointName)
 
-        if node == None:
-            node = self.attachNewNode(jointName)
+        if node is None:
+            node = partDef.partBundleNP.attachNewNode(jointName)
 
         if (joint):
             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) {
     DCField *field = get_inherited_field(i);
     if (field->as_molecular_field() == (DCMolecularField *)NULL &&
-        field->is_required()) {
+        field->is_required() && (field->is_ownrecv() || field->is_broadcast())) {
       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()) {
         break;
       }
@@ -946,19 +940,17 @@ ai_format_generate(PyObject *distobj, DOID_TYPE do_id,
   bool has_optional_fields = (PyObject_IsTrue(optional_fields) != 0);
 
   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 {
-    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
   // 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_uint16(_number);
-  packer.raw_pack_uint32(do_id);
 
   // Specify all of the required 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());
 }
 #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>.

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

@@ -117,11 +117,6 @@ PUBLISHED:
   Datagram client_format_generate_CMU(PyObject *distobj, DOID_TYPE do_id,
                                       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
 
 public:

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

@@ -391,7 +391,7 @@ Datagram DCField::
 client_format_update(DOID_TYPE do_id, PyObject *args) const {
   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_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_CHANNEL(to_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_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
 // 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
 

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

@@ -623,8 +623,8 @@ class SelectionRay(SelectionQueue):
     def pickBitMask(self, bitMask = BitMask32.allOff(),
                     targetNodePath = None,
                     skipFlags = SKIP_ALL):
-        if parentNodePath is None:
-            parentNodePath = render
+        if targetNodePath is None:
+            targetNodePath = render
         self.collideWithBitMask(bitMask)
         self.pick(targetNodePath)
         # 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
 
 
+__all__ = ["CRDataCache"]
+
 class CRDataCache:
     # 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().
         """
 
-        if msgType == CLIENT_CREATE_OBJECT_REQUIRED_OTHER:
+        if msgType == CLIENT_ENTER_OBJECT_REQUIRED_OTHER:
             # It's a generate message.
             doId = extra
             if doId in self.deferredDoIds:
@@ -263,7 +263,7 @@ class ClientRepositoryBase(ConnectionRepository):
             distObj.setLocation(parentId, zoneId)
             distObj.updateRequiredFields(dclass, di)
             # 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
 
     def generateWithRequiredOtherFields(self, dclass, doId, di,
@@ -381,7 +381,7 @@ class ClientRepositoryBase(ConnectionRepository):
             # The object had been deferred.  Great; we don't even have
             # to generate it now.
             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]
             if len(self.deferredGenerates) == 0:
                 taskMgr.remove('deferredGenerate')

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

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

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

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

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

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

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

@@ -146,8 +146,6 @@ class DistributedObjectAI(DistributedObjectBase):
                     barrier.cleanup()
                 self.__barriers = {}
 
-                self.air.stopTrackRequestDeletedDO(self)
-
                 # DCR: I've re-enabled this block of code so that Toontown's
                 # AI won't leak channels.
                 # 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
                 ### this delete message or to handle this message better
                 # 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.parentId = None
@@ -200,9 +197,6 @@ class DistributedObjectAI(DistributedObjectBase):
         """
         pass
 
-    def addInterest(self, zoneId, note="", event=None):
-        self.air.addInterest(self.doId, zoneId, note, event)
-
     def b_setLocation(self, parentId, zoneId):
         self.d_setLocation(parentId, zoneId)
         self.setLocation(parentId, zoneId)
@@ -274,9 +268,6 @@ class DistributedObjectAI(DistributedObjectBase):
 
         dclass.receiveUpdateOther(self, di)
 
-    def sendSetZone(self, zoneId):
-        self.air.sendSetZone(self, zoneId)
-
     def startMessageBundle(self, name):
         self.air.startMessageBundle(name)
     def sendMessageBundle(self):
@@ -349,10 +340,10 @@ class DistributedObjectAI(DistributedObjectBase):
             self.air.sendUpdate(self, fieldName, args)
 
     def GetPuppetConnectionChannel(self, doId):
-        return doId + (1 << 32)
+        return doId + (1001 << 32)
 
     def GetAccountConnectionChannel(self, doId):
-        return doId + (3 << 32)
+        return doId + (1003 << 32)
 
     def GetAccountIDFromChannelCode(self, channel):
         return channel >> 32
@@ -482,7 +473,6 @@ class DistributedObjectAI(DistributedObjectBase):
                 (self.__class__, doId))
             return
         self.air.requestDelete(self)
-        self.air.startTrackRequestDeletedDO(self)
         self._DOAI_requestedDelete = True
 
     def taskName(self, taskString):
@@ -581,3 +571,5 @@ class DistributedObjectAI(DistributedObjectBase):
         """ This is a no-op on the AI. """
         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.directnotify.DirectNotifyGlobal import directNotify
 
@@ -93,3 +92,11 @@ class DistributedObjectBase(DirectObject):
 
     def hasParentingRules(self):
         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)
 
     def GetPuppetConnectionChannel(self, doId):
-        return doId + (1 << 32)
+        return doId + (1001 << 32)
 
     def GetAccountConnectionChannel(self, doId):
-        return doId + (3 << 32)
+        return doId + (1003 << 32)
 
     def GetAccountIDFromChannelCode(self, channel):
         return channel >> 32

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

@@ -111,7 +111,7 @@ class DoInterestManager(DirectObject.DirectObject):
         self._allInterestsCompleteCallbacks = []
 
     def __verbose(self):
-        return self.InterestDebug or self.getVerbose()
+        return self.InterestDebug.getValue() or self.getVerbose()
 
     def _getAnonymousEvent(self, desc):
         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)
         datagram = PyDatagram()
         # Add message type
-        datagram.addUint16(CLIENT_ADD_INTEREST)
-        datagram.addUint16(handle)
-        datagram.addUint32(contextId)
-        datagram.addUint32(parentId)
         if isinstance(zoneIdList, list):
             vzl = list(zoneIdList)
             vzl.sort()
             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:
                 datagram.addUint32(zone)
         else:
-           datagram.addUint32(zoneIdList)
+            datagram.addUint16(CLIENT_ADD_INTEREST)
+            datagram.addUint32(contextId)
+            datagram.addUint16(handle)
+            datagram.addUint32(parentId)
+            datagram.addUint32(zoneIdList)
         self.send(datagram)
 
     def _sendRemoveInterest(self, handle, contextId):
@@ -530,9 +535,8 @@ class DoInterestManager(DirectObject.DirectObject):
         datagram = PyDatagram()
         # Add message type
         datagram.addUint16(CLIENT_REMOVE_INTEREST)
+        datagram.addUint32(contextId)
         datagram.addUint16(handle)
-        if contextId != 0:
-            datagram.addUint32(contextId)
         self.send(datagram)
         if __debug__:
             state = DoInterestManager._interests[handle]
@@ -583,8 +587,8 @@ class DoInterestManager(DirectObject.DirectObject):
         This handles the interest done messages and may dispatch an event
         """
         assert DoInterestManager.notify.debugCall()
-        handle = di.getUint16()
         contextId = di.getUint32()
+        handle = di.getUint16()
         if self.__verbose():
             print('CR::INTEREST.interestDone(handle=%s)' % handle)
         DoInterestManager.notify.debug(

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

@@ -3,104 +3,140 @@
 from direct.showbase.PythonUtil import invertDictLossless
 
 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.
-    '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

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

@@ -19,7 +19,7 @@ MsgName2Id = {
     'CLIENT_HEARTBEAT_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

+ 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 *
 # Import the type numbers
 
-#from otp.ai.AIMsgTypes import *
+from direct.distributed.MsgTypes import *
 
 class PyDatagram(Datagram):
 
@@ -47,13 +47,10 @@ class PyDatagram(Datagram):
         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):
         if (divisor == 1):

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

@@ -301,8 +301,8 @@ check_datagram() {
 
     switch (_msg_type) {
 #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 (_has_owner_view) {
           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_uint64(channel);
     dg.add_uint64(sender_channel);
-    dg.add_uint16(STATESERVER_BOUNCE_MESSAGE);
+    //dg.add_uint16(STATESERVER_BOUNCE_MESSAGE);
     // add each bundled message
     BundledMsgVector::const_iterator bmi;
     for (bmi = _bundle_msgs.begin(); bmi != _bundle_msgs.end(); bmi++) {
@@ -708,7 +708,7 @@ handle_update_field() {
       Py_DECREF(dclass_obj);
       nassertr(dclass_this != NULL, false);
 
-      DCClass *dclass = (DCClass *)PyLong_AsLong(dclass_this);
+      DCClass *dclass = (DCClass *)PyLong_AsVoidPtr(dclass_this);
       Py_DECREF(dclass_this);
 
       // If in quiet zone mode, throw update away unless distobj has
@@ -799,7 +799,7 @@ handle_update_field_owner() {
       Py_DECREF(dclass_obj);
       nassertr(dclass_this != NULL, false);
 
-      DCClass *dclass = (DCClass *)PyLong_AsLong(dclass_this);
+      DCClass *dclass = (DCClass *)PyLong_AsVoidPtr(dclass_this);
       Py_DECREF(dclass_this);
 
       // check if we should forward this update to the owner view
@@ -841,7 +841,7 @@ handle_update_field_owner() {
       Py_DECREF(dclass_obj);
       nassertr(dclass_this != NULL, false);
 
-      DCClass *dclass = (DCClass *)PyLong_AsLong(dclass_this);
+      DCClass *dclass = (DCClass *)PyLong_AsVoidPtr(dclass_this);
       Py_DECREF(dclass_this);
 
       // 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
     msg_type = packer.raw_unpack_uint16();
-    is_update = (msg_type == STATESERVER_OBJECT_UPDATE_FIELD);
+    is_update = (msg_type == STATESERVER_OBJECT_SET_FIELD);
 
   } else {
     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) {
@@ -974,7 +974,7 @@ describe_message(ostream &out, const string &prefix,
         Py_DECREF(dclass_obj);
         nassertv(dclass_this != NULL);
 
-        dclass = (DCClass *)PyLong_AsLong(dclass_this);
+        dclass = (DCClass *)PyLong_AsVoidPtr(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(_ai_id);
     // 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_uint16(field->get_number());
 
   } 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_uint16(field->get_number());
   }

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

@@ -132,6 +132,9 @@ class CommonFilters:
             if (len(configuration) == 0):
                 return
 
+            if not self.manager.win.gsg.getSupportsBasicShaders():
+                return False
+
             auxbits = 0
             needtex = set(["color"])
             needtexcoord = set(["color"])
@@ -339,7 +342,10 @@ class CommonFilters:
                 text += "  o_color = float4(1, 1, 1, 1) - o_color;\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:
                 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
             # NOTE: This overrides the DirectFrame text option
             ('initialText',     '',               DGG.INITOPT),
+            # Enable or disable text overflow scrolling
+            ('overflow',        0,                self.setOverflowMode),
             # Command to be called on hitting Enter
             ('command',        None,              None),
             ('extraArgs',      [],                None),
@@ -159,6 +161,9 @@ class DirectEntry(DirectFrame):
     def setCursorKeysActive(self):
         PGEntry.setCursorKeysActive(self.guiItem, self['cursorKeys'])
 
+    def setOverflowMode(self):
+        PGEntry.set_overflow_mode(self.guiItem, self['overflow'])
+
     def setObscureMode(self):
         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.direct import get_config_showbase
+from direct.showbase import ShowBaseGlobal
+from direct.showbase.ShowBase import ShowBase
 from . import DirectGuiGlobals as DGG
 from .OnscreenText import *
 from .OnscreenGeom import *
@@ -633,7 +634,7 @@ class DirectGuiBase(DirectObject.DirectObject):
         """
         # Need to tack on gui item specific id
         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
             print(gEvent)
             print(StackTrace())
@@ -662,7 +663,7 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
     # Determine the default initial state for inactive (or
     # unclickable) components.  If we are in edit mode, these are
     # actually clickable by default.
-    guiEdit = get_config_showbase().GetBool('direct-gui-edit', 0)
+    guiEdit = ShowBase.config.GetBool('direct-gui-edit', False)
     if guiEdit:
         inactiveInitState = DGG.NORMAL
     else:
@@ -723,21 +724,24 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
         if self['guiId']:
             self.guiItem.setId(self['guiId'])
         self.guiId = self.guiItem.getId()
-        if __dev__:
+
+        if ShowBaseGlobal.__dev__:
             guiObjectCollector.addLevel(1)
             guiObjectCollector.flushLevel()
             # 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
-        if (parent == None):
-            parent = aspect2d
+        if parent is None:
+            parent = ShowBaseGlobal.aspect2d
+
         self.assign(parent.attachNewNode(self.guiItem, self['sortOrder']))
         # Update pose to initial values
         if self['pos']:
@@ -1024,17 +1028,12 @@ class DirectGuiWidget(DirectGuiBase, NodePath):
 
     def destroy(self):
         if hasattr(self, "frameStyle"):
-            if __dev__:
+            if ShowBaseGlobal.__dev__:
                 guiObjectCollector.subLevel(1)
                 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
             for child in self.getChildren():
                 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))
 ##a = AmbientLight('cloudAmbientHi')
 ##a.setColor(Vec4(0.9, 0.9, 0.9, 1.000))
-##aNP = s.attachNewNode(a.upcastToPandaNode())
+##aNP = s.attachNewNode(a)
 ##b.setLight(aNP)
 ##d = DirectionalLight("chernabogDirectionalLight")
 ##d.setDirection(Vec3(0, 1, 0))
 ##d.setColor(Vec4(1))
 ###d.setColor(Vec4(0.9, 0.7, 0.7, 1.000))
-##dNP = s.attachNewNode(d.upcastToPandaNode())
+##dNP = s.attachNewNode(d)
 ##b.setLight(dNP)
 ##
 ##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 AppRunnerGlobal
 
-def legacyRun():
-    assert builtins.base.notify.warning("run() is deprecated, use base.run() instead")
-    builtins.base.run()
-
 @atexit.register
 def exitfunc():
     if getattr(builtins, 'base', None) is not None:
@@ -369,7 +365,6 @@ class ShowBase(DirectObject.DirectObject):
         builtins.bboard = self.bboard
         # Config needs to be defined before ShowBase is constructed
         #builtins.config = self.config
-        builtins.run = legacyRun
         builtins.ostream = Notify.out()
         builtins.directNotify = directNotify
         builtins.giveNotify = giveNotify
@@ -389,6 +384,12 @@ class ShowBase(DirectObject.DirectObject):
             builtins.aspect2dp = self.aspect2dp
             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__:
             ShowBase.notify.debug('__dev__ == %s' % self.__dev__)
         else:
@@ -396,10 +397,10 @@ class ShowBase(DirectObject.DirectObject):
 
         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
         if ConfigVariableBool('orig-gui-sounds', False).getValue():
@@ -511,9 +512,15 @@ class ShowBase(DirectObject.DirectObject):
 
         # Remove the built-in base reference
         if getattr(builtins, 'base', None) is self:
+            del builtins.run
             del builtins.base
             del builtins.loader
             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
         if self.config.GetBool('disable-sticky-keys', 0):
@@ -1097,13 +1104,18 @@ class ShowBase(DirectObject.DirectObject):
         self.render2d.setMaterialOff(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
         ## matches the window, but its coordinate system is square.
         ## This means anything we parent to render2d gets stretched.
         ## For things where that makes a difference, we set up
         ## aspect2d, which scales things back to the right aspect
         ## 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()
         self.aspect2d.setScale(1.0 / aspectRatio, 1.0, 1.0)
@@ -1260,8 +1272,7 @@ class ShowBase(DirectObject.DirectObject):
         if win == None:
             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())
         else:
             if win == None or not hasattr(win, "getRequestedProperties"):
@@ -1542,8 +1553,7 @@ class ShowBase(DirectObject.DirectObject):
             # mouse activity.
             mw = self.buttonThrowers[0].getParent()
             mouseRecorder = MouseRecorder('mouse')
-            self.recorder.addRecorder(
-                'mouse', mouseRecorder.upcastToRecorderBase())
+            self.recorder.addRecorder('mouse', mouseRecorder)
             np = mw.getParent().attachNewNode(mouseRecorder)
             mw.reparentTo(np)
 
@@ -2726,9 +2736,10 @@ class ShowBase(DirectObject.DirectObject):
             # changed and update the camera lenses and aspect2d parameters
             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.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:
                 xsize, ysize = self.getSize()
                 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__ = []
 
-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
 directNotify.setDconfigLevels()
 
+def run():
+    assert ShowBase.notify.warning("run() is deprecated, use base.run() instead")
+    base.run()
+
 def inspect(anObject):
     # Don't use a regular import, to prevent ModuleFinder from picking
     # it up as a dependency when building a .p3d package.
@@ -29,5 +50,4 @@ builtins.inspect = inspect
 
 # this also appears in AIBaseGlobal
 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:
- * 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
 
 #if PYTHON_FRAMEWORK
-  #include "Python/Python.h"
+  #include <Python/Python.h>
 #else
   #include "Python.h"
 #endif
@@ -85,6 +85,9 @@ static PyObject *_inP07ytsqGH(PyObject *self, PyObject *args);
 static PyObject *_inP07yt7shV(PyObject *self, PyObject *args);
 static PyObject *_inP07ytA1eF(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 *_inP07ytGB9D(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 *_inP07ytqHrb(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 *_inP07ytHu7x(PyObject *self, PyObject *args);
 static PyObject *_inP07ytwGnA(PyObject *self, PyObject *args);
@@ -1237,6 +1241,56 @@ _inP07yt776V(PyObject *, PyObject *args) {
   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
  * char const *interrogate_make_seq_num_name(MakeSeqIndex make_seq)
@@ -1397,6 +1451,20 @@ _inP07ytaOqq(PyObject *, PyObject *args) {
   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
  * char const *interrogate_type_name(TypeIndex type)
@@ -2416,6 +2484,9 @@ static PyMethodDef python_simple_funcs[] = {
   { "interrogate_wrapper_unique_name", &_inP07yt7shV, METH_VARARGS },
   { "interrogate_get_wrapper_by_unique_name", &_inP07ytA1eF, 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_element_name", &_inP07ytGB9D, 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_scoped_name", &_inP07ytqHrb, 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_scoped_name", &_inP07ytHu7x, 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