Go to QuArK Web Site
QuArK's Model Structure
Updated 05 Apr 2018
Upper levels:
QuArK Information Base
1. Introduction to QuArK
1.5. Model-editor in QuArK

 1.5.2. QuArK's Model Structure

 [ Prev - Up - Next ] 

This section covers more of the technical part, in detail, of QuArK's Model Structure for programming purposes and is very important to understand if you plan to do any code writing for the Model Editor. Occasionally references will be made to the Model Editor's Python code located in the quarkpy and plugins folders as well as areas in the 'QuarkX' section of these Infobase docs.


 Index


 Components of a Model

cdunde - 05 Apr 2018   [ Top ] 

A model is a single QuArK Internal Object that can consist of a single or multiple components. The elements that make up a model are stored as "key:value" pairs of the QuArK Internal Object, where there is a key name and a value that goes along with that key name.

Also, each of the "key names" CAN BE given a specific type to distinguish what kind of element it is.
So an example of a key, its type and its related value would look like this:

{'Skins:sg': <QuArK Internal object at 0x01390F78>}

The key name is Skins, its type sg (skin group) and its value is another QuArK Internal Object (the skin texture image). This method of using "key:value" pairs runs all the way through as a model's component(s) elements are broken down.

A list of the different "type"s of "key"s is shown in the code example below, displayed in blue.
This code is located in the quarkpy\mdlmgr.py file's def selchange(self): section of the class ModelLayout(BaseLayout): class
This code structures these items in the same way they are displayed in the QuArK Model Editor Tree-view:

fs = self.explorer.uniquesel
(what is selected at the moment.)
if fs is not None:
(to test that something IS selected.)
    fs.type == ':mg':
(misc. group [contains the :bound and :tag items shown further below within it])
    fs.type == ':tag':
(tag group [contains the individual :tagframe items shown further below within it if they exist])
    fs.type == ':bg':
(bone group [contains the bones, if they exist])
       fs.type == ':bone':
   (bones [if they exist])
        self.selectcgroup(fs)
    fs.type == ':mc':
(model component [contains the element groups shown within it])
        self.selectcomponent(fs)
    fs.type == ':sg':
(skin group [contains skins shown within it])
        self.selectcgroup(fs)
       fs.type == '.pcx':
   (skin [texture file])
           self.selectskin(fs)
       fs.type == '.tga':
   (skin [texture file])
           self.selectskin(fs)
       fs.type == '.dds':
   (skin [texture file])
           self.selectskin(fs)
       fs.type == '.png':
   (skin [texture file])
           self.selectskin(fs)
       fs.type == '.jpg':
   (skin [texture file])
           self.selectskin(fs)
       fs.type == '.bmp':
   (skin [texture file])
           self.selectskin(fs)
    elif fs.type == ':fg':
(frame group [contains the individual frames shown within it])
        self.selectcgroup(fs)
       fs.type == ':mf':
   (model frames [for animation])
           self.selectframe(fs)

the rest of this models types, not being displayed in the tree-view are:
    fs.type == ':sdo':       (system data object, not really used at this time)

The key name can be any arbitrary name, even the short path and name of the model skin file itself, as shown below:

{'models/characters/autopsy_d.tga': <QuArK Internal object at 0x01390F78>}

The QuArK Internal object, in the above example, is the actual autopsy_d.tga skin texture image file that is stored in memory and can be called upon to display in one of the editor's views by using its key name OR by giving the program functiion the related QuArK Internal object by using its key name. That all depends on what a particular program function needs, just the path and name or the actual image file, to perform its task.


 The Model's Mesh(s)

cdunde - 05 Apr 2018   [ Top ] 

A model is constructed from a mesh, which is a list of triangles grouped together to create its shape. The model can be a single mesh or it can consist of a number of meshes. In QuArK we call these meshes components. And therefore, each component of a model has its own group of triangles which are known as a components Tris Specific as stated in the Quarkx Model Editor section of these Infobase docs. Tris is an abbreviation for triangles.

Even though you can think of a component's Tris as a very complex poly, like those used in the QuArK Map Editor, and each triangle as a face of that poly, these triangles are handled differently in the Model Editor. However, like a poly face, each triangle has three vertexes arranged in a specific order which tells which side of that triangle face is facing outwards.

Instead of a set of 3 tuples of 3 values each (x, y and z) that give the 3 points of a triangle, the triangle of a component gives these values for its 3 tuples:

  1st item:  The triangle’s vertexnumber, also known as its index number, which can be used to call a specific triangle.
  2nd item:  The skin_s value for the horizontal position location of the models triangle on its skin texture image.
  3rd item:  The skin_t value for the vertical position location of the models triangle on its skin texture image.

This can be misleading if you do not understand the differences between the 3 tuples of a standard poly triangle and the triangle of a models component mesh or Tris. This is also discribed in the Quarkx Model Editor section of these Infobase docs.

The actual 3 point positions of a components triangle are given by calling the triangle's vertexnumber to retrieve its vertices list which is a set of 3 tuples of 3 values each (x, y and z) that give the 3 points for that triangle. Again, this is briefly covered in the Quarkx Model Editor section of these Infobase docs.

I realize all of this can be confusing, so let me give you an example using some actual code that is located in two related sections of the quarkpy\mdlhandles.py file that create and draw the handle for each vertex.

In this first section of code we start off by setting up a list that will be used to store all of the handles (line 1), as they are created and drawn.
Next we can see how the component.triangles is called to retrieve its Tris list and their tuple values (line 2).
Now we can call for the vertices of each triangle, in a loop function, by its index vertexnumber (lines 3 and 4).
Once we get one vertices of a triangle, we then go into another loop to retrieve each of the 3 vertex point values (x, y and z)
for one of the 3 points of that triangel (lines 5 and 6).
We then do two things as one, pass the data to the second section of code, which is covered further down,
and append (add) the returned handle to our h handle list (line 7). Which in this case is for the Skin-view handles.

(line 1)    h = [ ]
(line 2)    tris = component.triangles
(line 3)    for i in range(len(tris)):
(line 4)        tri = tris[i]
(line 5)        for j in range(len(tri)):
(line 6)            vtx = tri[j]
(line 7)            h.append(SkinHandle(quarkx.vect(vtx[1]-int(texWidth*.5), vtx[2]-int(texHeight*.5), 0), i, j, component, texWidth, texHeight, tri))

A print statement you can use to print it to the QuArK console is this:

print "SkinHandle ",quarkx.vect(vtx[1], vtx[2], 0), i, j
placed just under the code line above. The console will display 3 vertexes for each face like this:

SkinHandle   40 46 0 74 0
SkinHandle   59 48 0 74 1
SkinHandle   55 34 0 74 2
                            |     |   |    |   |
  2D pix. pos.  x    y  z    |   |
                                            |   |
         face index number   |
                                                |
          vertex order in a clockwise direction

One final thing you might want to notice is the very last item, or argument, that we pass to the class SkinHandle code section is the list of triangle vertices or tri itself.
This we will use in that code to do one other function, that of drawing the movement lines of a vertex handle, as a guide, while it is being dragged to change the position of the vertex and skin appearance on the model.

Now for the class SkinHandle part of the code, located above this code, in the same quarkpy\mdlhandles.py file, but I won't go through all of it here, for simplicity.

The class SkinHandle section consist of two main parts, the def draw and the def drag sections, the first of the two being simpler and more direct to the point I am making to the code. So we will take a look at the def draw section in part. One basic point about classes here is that we do not pass all of the above arguments directly to the def drag section itself, but to the class that it lies within. From the base class we can then pass it on to the actual def draw section by attaching the word self, meaning the class itself, and because the def draw is within that class we can then use self.triangle and use that arguments data in the draw section. Note how we also changed the arguments variable name from just tri to triangle to make it more clear in the code as to what that item really represents (or actually contains).

Now , in the def draw section of code, we will just look at a few lines intermittently to see how it uses this particular argument.

First we obtain it from the base class arguments that it receives and redefine it with a new name (line 1).
Next, because it already is the list of triangle vertices that we need, we simply use a loop to go through that list, one vertex at a time, and get the data for each vertex of the triangle that we need to draw the vertex dragging guide lines (line 2).

This next part is a little bit tricky to understand, so bear with me.
In the def draw section, it receives an argument self also, which in the def draw case, IS the vertex handle that we are dragging, and also position 0 of the triangle.
It is also located in the list of triangle vertices along with the triangles two other vertex positions, 1 and 2. Items like this start with a count of 0 instead of 1 when it comes to programming. So the two other vertexes represent the stationary vertexes of the triangle that are NOT being dragged, but are needed to draw the lines from the one that is being dragged, and to give us our guide line fixed positions for drawing in a lime green color so they are easily seen in the Skin-view as shown in the screen shot.

So with that out of the way, we pull those stationary vertexes out of the list, which is a tuple of 3 vertexes (pos 0, 1 and 2), and define each vertex as a vector called fixedvertex (line 10), skipping the first one (lines 3-6) because that is our self drag handle. Which we already have and will be drawing our guide lines from.

Something else you might have noticed here is that we do not use position 0 and 1 (x and y) here as you might think, but positions 1 and 2 (y and z) instead.
vertex[1]...vertex[2]
Don't ask me why, it's just the way the quarkx.vect function works.

Once we have defined our vector it also becomes a tuple, so now we need to pull out that vertex's positions for X , Y and Z (line 11).
Because these positions are actually pixels on a flat 2D screen (your monitor), with 0,0 being at the upper left hand corner of the Skin-view, the value for Z is not needed.
So we just ignore that one and use the X and Y positions for the fixed end of a line and the X and Y positions of our vertex handle for the other end( line 12).

Remember, this is done for each line that is drawn as the handle vertex and its triangle are passed to the class SkinHandle from the first section of code. Looking at the actual and complete code in the file will fill in the gaps and help answer any questions of other items shown below.

(line 1)    triangle = self.triangle

(skipping a few lines of code)

(line 2)        for vertex in triangle:
(line 3)            if self.ver_index == 0:
(line 4)                if count != 0: break
(line 5)                count = count + 1
(line 6)                pass
(line 7)            else:
(line 8)                if count > 2: break
(line 9)                count = count + 1
(line 10)              fixedvertex = quarkx.vect(vertex[1]-int(texWidth*.5), vertex[2]-int(texHeight*.5), 0)
(line 11)              fixedX, fixedY,fixedZ = view.proj(fixedvertex).tuple
(line 12)              cv.line(int(pv2[0]), int(pv2[1]), int(fixedX), int(fixedY))

And in the screen shot below you can see these vertex handles for the component deadeye Tris as they are displayed in both the Model Editors views and its 'Skin-view page'. Just remember though, the handles in the Model Editor views, which move the components mesh for the models shape, are NOT the same handles that are shown on the 'Skin-view page', which move the unwrapped components mesh for skinning purposes. So one handle type will NOT effect the other. But both are created and drawn in the same way, they just wind up in different handle lists.


 The Skin-view Mesh(s)

cdunde - 05 Apr 2018   [ Top ] 

A model is constructed from a mesh, which is a list of triangles grouped together to create its shape. The model can be a single mesh or it can consist of a number of meshes. In QuArK we call these meshes components. And therefore, each component of a model has its own group of triangles which are known as a components Tris Specific as stated in the Quarkx Model Editor section of these Infobase docs. Tris is an abbreviation for triangles.

The Skin-view page will display an unwrapped, or flat version, of each of the model's components when it or any of its sub-items have been selected in the tree-view.

The Skin-view page works a bit defiantly then the editor's views do, based on the fact that it has no actual triangles. Only what is known as view.handles, view being the view displayed on the Skin-view page itself. Another big difference, at this time, is that unlike all of the other views of the editor which are in a list of its editor.layout.view, the Skin-view page view is never actually added (appended) to that list, on a permanent bases, making it pretty much isolated from the rest of the views of the editor. Because of this I needed to create a global in the quarkpy\mdlhandles.py file called SkinView1 that can be used, when needed, to call that view when the Skin-view page is open. It does seem to be added periodically when the mouse cursor is actually somewhere within the Skin-view page view.

As briefly described in the Internal objects related to the Model Editor of the 'QuarkX' section of these Infobase docs, the Skin-view page view uses a model's Component, Frame object's vertices, also known as its views view.handles. There is a lot being said in that last part, so let me break it down for you in more layman's terms as to what it all means exactly.

  Component :  Each model can be made up of one or more Components, or parts, each containing sub-items, as described in the Components of a Model section above, one of which is the frame group that contains that particular components animation frames that gives that component its movement sequences.

  Frame object's vertices :  Each frame has attached to it a list of vertexes or points in 3D space that make up that components Frame, or mesh of triangles for its shape. These vertexes are the Frame object's vertices which are also the Skin-view page view.handles. Below is a very small sample of a Skin-view view.handles list.

[<quarkpy.mdlhandles.SkinHandle instance at 0x0101AA08>,<quarkpy.mdlhandles.SkinHandle instance at 0x0101AA58>]

Each item in this list, as you can see, is an instance of a quarkpy.mdlhandles.SkinHandle and if you look at that Class SkinHandle in the quarkpy\mdlhandles.py folder\file you will see that each instance has these items, or Specifics that make up that SkinHandle, or Frame object's vertices point.

def __init__(self, pos, tri_index, ver_index, comp, texWidth, texHeight, triangle):

   self  ----------- being the handle or quarkpy.mdlhandles.SkinHandle instance itself.
   pos  ----------- that handle's 3D position in space when it was created or changed.
   tri_index  ---- the triangle number of the component that view.handle belongs to.
   ver_index  -- the vertex position of that triangle in sequential order, 0, 1 or 2.
   comp  -------- the model component that triangle and view.handle is a part of.
   texWidth  ---- the computed width of the texture skin image that component uses.
   texHeight  --- the computed height of the texture skin image that component uses.
   triangle  ------ the triangle itself that view.handle is a part of.

The data any one of these items contain can be used by simply adding its Specific name to the view.handle like this:

vtxpoint = view.handle.pos
These view.handle positions are then used to draw the handles onto the Skin-view's view and the lines that make up that components Skin mesh, but there are still no triangles per-say. These Specifics data can also be used to pass the needed items data from the Skin-view page to the editor's views and visa-versa as seen in the quarkpy\mdlutils.py file for the PassSkinSel2Editor and PassEditorSel2Skin functions which are located there, you might want to review those as additional detailed information for those functions use, explanation and data format are also located there for each function along with a simpler formula for the PassEditorSel2Skin function that can expedite its application time for obtaining data that it needs.

There are two other functions, as well as several more useful ones, in the quarkpy\mdlutils.py file that you should also be aware of called:

Update_Editor_Views:  Updates the Editors views once something has changed in the Skin-view, such as synchronized or added 'skin mesh' vertex selections. It can also be used to just update all of the Editor's views only.

MakeEditorFaceObject:  Creates a QuArK Internal Face Object from 3 selected vertexes in the ModelVertexSelList. This one is unique to the Model Editor in that it uses the same structure as a Map Editor QuArK Internal Face Object, Which should allow the calling of various Map Editor files and functions in the Python code files to use in the Model Editor once the returned items from those functions are converted back into usable data in the Model Editor required format.

Both of the above functions have various option settings that can be applied for more specific uses and those option settings and descriptions are in each of their related function description section.

One last item to cover are the various selection lists that I have setup for the Model Editor. Each one being specific for their needs and uses.

ModelVertexSelList:  Used for individual and multi selected Model Editor component mesh vertexes.

SkinVertexSelList:  Used for individual and multi selected Skin-view skin mesh vertexes.

ModelFaceSelList:  Used for individual and multi selected Model Editor component mesh triangles (faces).

SkinFaceSelList:  Used for passing and retaining individual and multi selected Model Editor component mesh triangles (faces) to the Skin-view page at this time.

Complete detailed descriptions of each one of these selection lists are given near the top of the quarkpy\mdleditor.py file along with their related list item formats as to what data they require and carry as well as to what their purpose are. It would be a good idea to review that information as more will be added during future development of this editor.

One vital point about the SkinVertexSelList list is that it must be changeable, or mutable in Python terms, and the added data within it, or the list will become ineffective and objects will not be able to be updated when moved to work with QuArK. They will simply DISAPEAR !

To make this list mutable it must use square brackets to define it and any items that are added to that list as well, like this:

SkinVertexSelList = []
SkinVertexSelList = SkinVertexSelList + [pos, self, tri_index, ver_index]

In addition to the above selection lists there are other lists used by the Model Editor, two of which are:

view.handles:  Covered in detail above earlier.

comp.filltriscomp being the currently selected model component or editor.Root.currentcomponent and the actual list filltris which is a list of item settings to draw a component's triangle filled with a specified color and which is also described in the Internal objects related to the Model Editor of the 'QuarkX' section of these Infobase docs. Also doing a word search using comp.filltris will divulge its usage in the Python quarkpy\mdleditor.py file.

I realize there is a lot more relating to the editor but this very vital basic works information should help to get acquainted with the QuArK Model Editor inter actions much easer.



Copyright (c) 2022, GNU General Public License by The QuArK (Quake Army Knife) Community - https://quark.sourceforge.io/

 [ Prev - Top - Next ]