For those scratching their heads:
- Show Spoiler
The gate to the palace courtyard has disappeared!
Being threatened with terminal boredom because I have been working for weeks on a security analysis, one night I decided to put everything aside and work on solving my most irritating hurdle in Prp hacking: deleting visible objects.
This has bugged me since my "Opening the Cavern" thing 2 years ago (remember that we had to walk through visible doors), and it was the last obstacle for UruDistro. It is no use making changes to a prp if you can't get rid of old objects.
From past experiments I knew it was no use deleting plSceneObjects or plDrawInterfaces, since everything is pre-calculated at the time of exporting. The answer lies in my old nemesis: plDrawableSpans. As far as the Plasma rendering engine is concerned, this is the Age. The rest of the prp is just modifiers.
The Plasma engine already has methods for preventing the rendering of an object. If the object has a coordinate interface, you can use Python methods for enabling and disabling the object. You can add "show" and "hide" commands to your KI:
- Show Spoiler
- Code: Select all
if(cmnd=="show"):
print "show:"+arg
obj = PtFindSceneobject(arg,PtGetAgeName())
obj.draw.enable()
obj.physics.suppress(false)
ki.IAddRTChat(None, ('Object %s appears' % arg), 0)
return True
if(cmnd=="hide"):
print "hide:"+arg
obj = PtFindSceneobject(arg,PtGetAgeName())
obj.draw.disable()
obj.physics.suppress(True)
ki.IAddRTChat(None, ('Object %s disappears' % arg), 0)
return True
EDIT: those commands are now part of Offline-Ki 3.4. (but I'm not claiming responsibility for it).
But there seems to be no way of doing that in the prp file. The plCoordinateInterface and plDrawInterface objects have a Disable flag, but setting it in the prp has no effect.
So I started studying plDrawableSpans, reading source from libPlasma, pyPrp, Age Builder and Drizzle. This helped me understand the structure of the object, that everyone but me knew already. My limited knowledge of object-oriented programming and modern languages made this difficult. I also reinstalled Blender on my new PC and started making test Ages. Zrax's libPlasma tools were invaluable for that, along with an XML editor and a knowledge of XPATH syntax.
Soon I understood enough of the structure, even though I don't have all the details.
In short:
Each visible Plasma object is made out of one or several meshes that are made out of vertices and triangles (Blender's faces). When exporting to a prp from Blender or the Cyan plug-in, the exporter breaks down each object into meshes according to the material used on it. Each mesh has one material.
In the plDrawableSpan object, each mesh is described by a structure called a plIcicle. The whole data structure is shown in XML like in this simplified example:
- Show Spoiler
- Code: Select all
<plDrawableSpans>
<plKey/>
<Properties/>
<Materials>
...
</Materials>
<Icicles>
<plIcicle>
<SpanInfo SubType="1" Material="32" Properties="0x00000000" />
<VertexSpan GroupIdx="0" BufferIdx="0" CellIdx="0" CellOffset="0" StartIdx="0" Length="21" />
<Icicle BufferIdx="0" StartIdx="0" Length="21" />
</plIcicle>
</Icicles>
<DIIndices>
<plDISpanIndex>
<Index value="245" />
<Index value="224" />
<Index value="8" />
</plDISpanIndex>
</DIIndices>
<BufferGroups>
<plGBufferGroup>
<VertexGroup>
<Vertex>
<Position>
<hsVector3 X="236.3417969" Y="49.58007813" Z="302.1748047" />
</Position>
<SkinWeights></SkinWeights>
<Normal></Normal>
<Color value="0xFF000000" />
<UVWMaps></UVWMaps>
</Vertex>
</VertexGroup>
<IndexGroup>
<Triangle>0 1 2</Triangle>
<Triangle>2 3 0</Triangle>
</IndexGroup>
<CellGroup>
<plGBufferCell VertexStart="0" ColorStart="4294967295" Length="16674" />
</CellGroup>
</plGBufferGroup>
</BufferGroups>
<SpaceTree>
...
</SpaceTree>
<SceneNode>
</SceneNode>
</plDrawableSpans>
For each plIcicle, the "SpanInfo" node provides an index into the "Materials" node. Here, Material = "32" means the 32nd material in the list (counting from 0). It is thus possible to add an empty material to the Age (fully transparent texture), then point plIciles to it. They would be invisible, but Plasma would still render them, which isn't efficient.
The "VertexSpan" node is an index for finding the vertices for the mesh in the "BufferGroups" node. This is useless for our purposes. Well, maybe we could change the x,y,x coordinates for the vertices so the meshes are drawn out of sight, but that would be an ugly solution.
Now comes the interesting "Icicle" node: <Icicle BufferIdx="0" StartIdx="0" Length="21" />
Using a start index and a length is a pretty standard way for pointing to a part of a data structure. It reminds me of my old days of C programming. I started thinking, what if I told Plasma that the length is 0? It would not break the structure (modifying one plIcicle would have no impact on the others) and it would be efficient.
So I changed it to 0, used prcc and PrpShop to bring the changed plDrawableSpans object to the City palace prp, and presto! No more gate.
Now the trick is to find out which plIcicles to change. As the tool makers all now, this part is simple, although tedious when you do it manually:
1- In prpShop, open the plDrawInterface for your target object. This tells you the name of the plDrawableSpans object(s) that you want to modify. It also gives you the index inside each plDrawableSpans object. Let's say we get "53".
2- Still in prpShop, open the plDrawableSpans into the PRC editor. Cut and paste the whole XML file to your XML editor.
3- In your XML editor, find the 54th <plDISpanIndex> node (not 53rd, because the XML editor starts counting at 1. Always remember to add 1 to the index. During my experiments, I accidentally the whole Palace). Using the Microsoft XML editor, I do a search on this XPATH expression: /plDrawableSpans/DIIndices/plDISpanIndex[54].
4- Open this node. Here you find the indices for all plIcicles in your object.
In the example above, you get this:
- Code: Select all
<plDISpanIndex>
<Index value="245" />
<Index value="224" />
<Index value="8" />
</plDISpanIndex>
You can then change all or some of them.
5- To find plIcicle number 245, use this XPATH: /plDrawableSpans/Icicles/plIcicle[246]. Open the <Icicle> node.
6- Change the value for the "Length" attribute to "0". In the above example, change "21" to "0".
7- Save the file in your XML editor, use prcc -v POTS [Name of the XML file] to compile it to out.po.
8- In Prpshop, open the target prp, delete the old plDrawableSPans object, import out.po, save the file.
9- In PlasmaShop, open the sum file. Either recalculate or delete all entries.
10- Start Uru and test your changes.
This is the simplest way I have found for deleting a visible object in a prp. It is simple enough to do it by hand, but it is tedious. The next step is to automate this using my Urudistro scripts and XML worker functions, which is trivial.
EDIT: This is done. See
here.
It is even possible to easily roll back the prp. It would be easy for more knowledgeable programmers to make a libPlasma tool for deleting an object.
A more elegant way would have been to rewrite the whole plDrawableSpans object, filtering out unneeded part, the way Dustin does it with his Drizzle distiller. But it seemed too much effort for my purposes.
Testing was easy because I have .blend files of the City prps, all imported with PyPrp 0.5 which imported meshes as found in plDrawableSpans, broken up by material. So each plIcicle matched one object in Blender, in the same order and with the correct material.
I have found no adverse effect of using this technique during my tests.
Maybe someone knows of a better or simpler way to do this, but I haven't found one. Either way, finding this solution was fun, challenging and I learned something!