Jump to content
The Dark Mod Forums

Using a spreadsheet for cutting patches


RJFerret

Recommended Posts

I've added on to AluminumHaste's original instructions on patch splitting with how to do the actual cutting in a spreadsheet, where the grid makes it a bit more straightforward to do either direction.

  • Like 3

"The measure of a man's character is what he would do if he knew he never would be found out."

- Baron Thomas Babington Macauley

Link to comment
Share on other sites

  • 1 month later...

I did a python script that splits patches in the editor yesterday, after finding I'd made the horrible newbie error of making the entire exterior floor of my map from just 3 patches. They'd have been hit by about 20 lights each if I hadn't split them, and I'd already painstakingly stitched them together so didn't want to restart.

 

Is it worth me turning this into a more user-friendly tool? At the moment it's just a barebones scripting capability: Undo doesn't work consistently so you have to save first, and there's no GUI. You choose a row or col to split on and issue a 1-liner command in the python console. But it does split patches seamlessly using almost the same method as the text editor / spreadsheet options, and it's very easy and quick to carve up a patch: just turn on vertex mode so it's easy to count the pink dots, put that number in the panel and hit run script.

 

Not sure how much it's needed though. I for one won't be making that same newbie error again. Or perhaps I will come to think of it... making one big patch, curving it and adding noise then splitting it is a lot faster than making lots of small patches and stitching them. It took me less than 5 mins to carve up my bumpy curved floor.

 

I'll paste the code here for reference as I'll be on holiday for a week from this evening and although I'll still be checking in to the forum, I won't be able to get back the code.

 

 

 

class PatchSplitter:
def __init__(self):
	self.patch = self._getSelected()
	self.cols = self.patch.getWidth()
	self.rows = self.patch.getHeight()
	self.newpatch = None

	self.verts = []
	for row in range(self.rows):
		rverts = []
		for col in range(self.cols):
			rverts.append(self.patch.ctrlAt(row, col))
		self.verts.append(rverts)

# private functions
def _getSelected(self):
	patch = GlobalSelectionSystem.ultimateSelected().getPatch()
	if patch.isNull():
		raise Exception('patch must be last selection')
	return patch

def _slicerow(self, r):
	return self.verts[:r+1], self.verts[r:]

def _slicecol(self, c):
	return [r[:c+1] for r in self.verts], [r[c:] for r in self.verts]

def _createNew(self):
	# reselect to clear vertex editing mode
	self.patch.setSelected(0)
	self.patch.setSelected(1)
	GlobalCommandSystem.execute('CloneSelection')
	self.newpatch = self._getSelected()

def _adjustPatch(self, p, v):
	p.setDims(len(v[0]), len(v))
	for row in range(len(v)):
		for col in range(len(v[0])):
			p.ctrlAt(row, col).vertex = v[row][col].vertex
			p.ctrlAt(row, col).texcoord = v[row][col].texcoord
	p.controlPointsChanged()

def split(self, row=-1, col=-1):
	if 0 < row < self.rows and row%2 == 0:
		slicer = self._slicerow
		arg = row
	elif 0 < col < self.cols and col%2 == 0:
		slicer = self._slicecol
		arg = col
	else:
		errmsg = 'Error: choose an even numbered row or col (pink ' + \
				 'verts). NB first row/col is numbered 0'
		raise Exception(errmsg)
	self._createNew()
	a, b = slicer(arg)
	# always do new patch first as our self.verts reference will be
	# mutated when we adjust original patch
	self._adjustPatch(self.newpatch, 
	self._adjustPatch(self.patch, a)
	self.patch.setSelected(1)
	self.newpatch.setSelected(1)


# USAGE:
# Select a patch then work out which row/col you want to split on.
# Numbering is as seen in the patch inspector
# Needs to be a proper vertex row/col, i.e. pink verts / even
# numbered in the inspector
# Examples:
#	PatchSplitter().split(col=2)
#	PatchSplitter().split(row=4)

 

 

Link to comment
Share on other sites

Sounds like something I'd be interested in, if it's user friendly enough. Road patches are a pain.

Link to comment
Share on other sites

GUI would matter, that's the benefit of the spreadsheet, giving you essentially a visual rectangle matching the patch points, and letting you just drag select, cut, paste, etc.

 

The only liability is you have to be aware enough to maintain an odd number of row/columns, which obviously should be checked for in a tool, as well as limited integration with DR.

 

It is true that getting past newbie understanding allows users to accommodate the current lack of tools, but useful tools will open up more flexible patch construction to others and ease it for the rest of us.

 

PS: Patch surfaces might not have the same liability as brush surfaces with lighting due to their smaller triangles instead of extending all the way across a brush.

"The measure of a man's character is what he would do if he knew he never would be found out."

- Baron Thomas Babington Macauley

Link to comment
Share on other sites

I did a python script that splits patches in the editor yesterday, Is it worth me turning this into a more user-friendly tool?
Sounds like something I'd be interested in, if it's user friendly enough.

YES please! and if it can be used in DR that would be bloody awesome! For example, imagine a cylinder with a spyral stair case inside, and you want top cut rectangular hols in it for the doorways etc - this is your script was pay in spades!

 

How do we use your existing script, where do we put that in the DR directory and how is it used.?

Link to comment
Share on other sites

@RJFerret: the code already has that safeguard built in even at this stage :)

 

@Biker: you can try it right now without needing to save anything as a file. Just go to the python console in DR and paste in the entire code block above. Nothing will happen at that point, even if you press "run script". Then when you want to use it, select a patch, work out what row or column you want to split on, and in the python console add one of these extra lines (below the code you pasted in):

 

PatchSplitter().split(col=NNN)

PatchSplitter().split(row=NNN)

 

Depending whether you want to split on a row or column. Replace NNN with the number. Then hit "run script".

 

Like in the patch inspector, the bottom edge of the patch is row 0. The next row of pink verts above that is row 2, and so on. You can't split on an odd-numbered row (which has only green verts).

 

It's a little awkward to use, but I was going at a fast pace with a clip every few seconds once I'd got started.

 

Thanks for the quick responses guys.. yes in that case I'll finish the job when I get back next week :) This post was hot off the press after I nuked a bug this morning and actually used it to cut up my map. I've already thought of a way to address the undo problem (in fact it might have been fixed in a change I made earlier), and of one way I might be able to make it purely point-and-click. How friendly the GUI can be will depend on what info I can get, if any, about selected vertices in DR's python API.

 

What I don't know is whether it's possible to add a menu item to DR, or call a python script from a keyboard shortcut just through config file changes. I'll do my research!

 

EDIT: @Biker, yes, good point, it could do that pretty straightforwardly. A use case I never thought of.

Edited by SteveL
Link to comment
Share on other sites

  • 3 weeks later...

Remembered I'd left this thread dangling so I'll add an update. It's been waiting for me to dig into the DR C++ code for the python api to see what if any means there is to get info about selected components, but I haven't got round to that again yet. I'll post here in case someone knows the answers, and if not, no worries I'll come back to it.

 

After getting back from hols I had trouble coming up with a way to make this work purely graphically. Specifically, a way for the mapper to mark the verts where he wants the patch cut. When I made the last post, I'd spotted that the API provides a way for you to cycle through selected components (faces and verts) using the python api, so thought I had an answer. The mapper could select a couple of verts, and if they were lined up on a valid place to cut the patch, hey presto it would work. Unfortunately I couldn't get the coords of the verts selected, I could only count them. You can request their bounding box but it's null.

 

I think I missed something because I couldn't spot any way to do anything with a selected component once you've got a reference to it... I probably missed something, or it might be an unfinished part of the api.

 

Either way this is still on my to-do list because the patch cutting trick works a treat. If anyone knows the answer or another point-and-click way for a mapper to mark a line on a patch, please shout.

Link to comment
Share on other sites

I think the simplest and easiest way to support a patch cutting utility, cmd prompt or gui or otherwise, would be to just add a visual cue to the patch inspector that selects vertices of a whole column, row, or just a single vertex based on row+col. Would make it about 10000x easier to determine the lines you want to cut at if you've already messed around with the patch a lot. I look forward to the day i can split an inverted endcap in half easily just by checking the the visual layout of rows/cols before plugging in the row or col i want to be the seam into a patch split tool like this. so many possibilities.

 

edit: or, additionally, add a toggle option that puts a visual display of the RC pair like example: (2,3) next to each vertex. Or if multiple vertices share the same space, put an ordered list like {(0,0), (1,2), (2,3)} or something that shows all verts in that space. And holy crap, if you could mouse over the rc pair and select a single vert you want just by clicking on that rc pair... man. would change how we work with patches dramatically forever.

Edited by ungoliant
Link to comment
Share on other sites

  • 2 weeks later...

I've been playing with this lately, and yea, all references to components can only get you info about the patch it belongs to. Even the Patch Inspector only yields information about a component that you provide explicit col/row info for.

 

Still having trouble trying to get meaningful data out of the sourcecode, but the texture tool code might provide some clues.

You can manually select texcoords for multiple corresponding verts, and modify the values of the selection just by dragging it around. the twin-sister sibling of the texcoord is the vertex, both of which belong to a PatchCoord parent. If you could get your hands on the Vector3 data of selected vertices in a similar fashion, then you can find a way to compare it to the PatchMesh vertices structure of the selected patch, and yield row/col data out of that.

 

edit: actually i guess looking at texturetool would be pointless, as you can just look at translate, rotate, or other tools that modify actual vertex position. Important thing is just finding a way to give access to the PatchControl object to python scripts.

Edited by ungoliant
Link to comment
Share on other sites

If you've got a patch control for each selected component, it should be a done deal. for each component compare and contrast the vector3 data in PatchControl.vertex against the vector3 data of all verts in the patch that are held in node.getTesselatedPatchMesh().vertices[row*col + col].vertex

Link to comment
Share on other sites

Agreed, but I haven't linked the two. I can get a full set of patch controls for the patch, and the selected components, but haven't found a way to match them up.

 

I admit I've not been back to the c++ code since my first attempt. It might be that something's exposed but not documented, or that a small tweak to the python API could enable it. Fingers crossed anyway. I'll go take another look when I've done messing with the modwiki dump, and trying to fix the clouds in my skybox.

Link to comment
Share on other sites

not all that great with c++ but i've been poking around. Working under the assumption that traversing components with GlobalSelectionSystem.foreachSelectedComponent(SelectionVisitor visitor) is the only way to get this done. I can't find anything undocumented here that might help. I think it's gotta be coded. heres my findings:

class SelectionVisitorWrapper :
   public SelectionSystem::Visitor,
   public boost::python::wrapper<SelectionSystem::Visitor>
{
public:
   void visit(const scene::INodePtr& node) const {
       // Wrap this method to python
       this->get_override("visit")(ScriptSceneNode(node));
   }
};

nspace["SelectionVisitor"] = boost::python::class_<SelectionVisitorWrapper, boost::noncopyable>("SelectionVisitor")
       .def("visit", boost::python::pure_virtual(&SelectionSystem::Visitor::visit));

as far as I can tell, with patch verts selected, this just gives access to the patch node itself inside the visit function. If a PatchControl object for each component could be passed during each visit along with the node, problem is solved. i'm not good enough at coding to do this though.

Link to comment
Share on other sites

  • 3 weeks later...

EUREKA!!!

 

You can't find out which patch verts are selected directly, but a script *can* move the selected verts around using the standard ALT+arrow key bindings. And that has an effect on the patch's overall bounding box, which you can test in the script. So with a bit of jiggery pokery you can work out what the bounding box of the selected verts was at script start, or in other words, it'll tell you where the user wants the patch to be cut. It won't matter that the patch gets twisted up: it can be put back again exactly.

 

EDIT: @ungoliant: Sorry, I missed your last post in Feb. Agreed, that's the better way to go about it. I'll try to get this method working as a stop-gap until we can come up with a fully fleshed-out proposal for an API tweak. The horrendousness of what's going on under the hood with the method I'm about to test should be invisible to the user...

Edited by SteveL
Link to comment
Share on other sites

Testing so far so good... Some time this weekend if nothing goes pear-shaped. Wish me luck, I'm not leaving this spot to eat or drink or anything until it's done! I still have some testing to do and I'll have to do some tidying up like assigning the new patch to the right FS and Layer... and anything else I haven't thought of. Suggestions please guys if anything else that'll need fixing occurs to you.

 

The code that actually does the patch splitting is tested and works great, which is why I sound confident. I've been using that myself for 2 months now. I couldn't have made that fancy well cover for the moonlight demo without it. This is just about finding a way to make it usable without typing in code. If the above works, you'll just have to select 2 verts along the line where you want the patch cut and click "Split patch".

  • Like 1
Link to comment
Share on other sites

<3 <3 <3~!!!!

 

Now if we had a way to weld/unweld verts on neighboring patches and vertex paint texture in the editor in real-time without going through placing brush objects and exporting! (and on a side note, being able to setup Cordon Bounds in a map for troubleshooting would be freaking wicked.)

 

SteveL is amazing is amazing.

Edited by Lux
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recent Status Updates

    • taffernicus

      i am so euphoric to see new FMs keep coming out and I am keen to try it out in my leisure time, then suddenly my PC is spouting a couple of S.M.A.R.T errors...
      tbf i cannot afford myself to miss my network emulator image file&progress, important ebooks, hyper-v checkpoint & hyper-v export and the precious thief & TDM gamesaves. Don't fall yourself into & lay your hands on crappy SSD
       
      · 2 replies
    • OrbWeaver

      Does anyone actually use the Normalise button in the Surface inspector? Even after looking at the code I'm not quite sure what it's for.
      · 7 replies
    • Ansome

      Turns out my 15th anniversary mission idea has already been done once or twice before! I've been beaten to the punch once again, but I suppose that's to be expected when there's over 170 FMs out there, eh? I'm not complaining though, I love learning new tricks and taking inspiration from past FMs. Best of luck on your own fan missions!
      · 4 replies
    • The Black Arrow

      I wanna play Doom 3, but fhDoom has much better features than dhewm3, yet fhDoom is old, outdated and probably not supported. Damn!
      Makes me think that TDM engine for Doom 3 itself would actually be perfect.
      · 6 replies
    • Petike the Taffer

      Maybe a bit of advice ? In the FM series I'm preparing, the two main characters have the given names Toby and Agnes (it's the protagonist and deuteragonist, respectively), I've been toying with the idea of giving them family names as well, since many of the FM series have named protagonists who have surnames. Toby's from a family who were usually farriers, though he eventually wound up working as a cobbler (this serves as a daylight "front" for his night time thieving). Would it make sense if the man's popularly accepted family name was Farrier ? It's an existing, though less common English surname, and it directly refers to the profession practiced by his relatives. Your suggestions ?
      · 9 replies
×
×
  • Create New...