There's no need ...
I've made some changes - both large and small - to the code.
It now also:
- Doesn't crash if no vertices are removed

- Welds a cylinder properly without losing data
- Welds a memblock produced mesh
- Recalculates all normals from scratch
First, there was an off-by-one error in my code for the index data. That's why the lost data on cylinders (and hopefully for DBO's too).
Next, I've done some highly dodgy object manipulation by calling internal routines from the Basic3D DLL, and using Peek/Poke functions - I've deliberately stayed away from using my plug-ins for this so hopefully I haven't introduced any dependencies.
This allowed me to locate the mesh data in memory so that I can call routines a) to convert the mesh to tri-list format if it isn't already, b) to generate index data (in a highly dodgy way IMO) if there isn't any already there, and c) to recalculate normals - you'll see that I've cut out all code that dealt with normals as there's no need for it any more.
To be honest, this probably takes it out of the range of most people to modify, but it should still be relatively easy to use if all they want to do is weld vertices.
sync on
sync rate 60
ink rgb(0, 0, 0), rgb(0, 0, 0)
make object cylinder 1, 2.0
`make mesh from object 1, 1
`make memblock from mesh 1, 1
`make mesh from memblock 1, 1
`change mesh 1, 0, 1
WeldLimbVertices(1, 0)
repeat
set cursor 0, 0
lock vertexdata for limb 1, 0
print "get vertexdata vertex count() = "; get vertexdata vertex count()
print "get vertexdata index count() = "; get vertexdata index count()
unlock vertexdata
print ""
rotate object 1, 0.0, wrapvalue( object angle y(1) + 1.0 ), 0.0
sync
until spacekey()
end
type Vertex_t
x as float
y as float
z as float
u as float
v as float
Diffuse as dword
OriginalPosition as integer
UsageCount as integer
endtype
type VertexInfo_t
Vertex as Vertex_t
OriginalPosition as integer
Matches as integer
UsageCount as integer
endtype
global dim Vertex() as VertexInfo_t
global dim Index() as integer
function WeldLimbVertices(Object as integer, Limb as integer)
local V as Vertex_t
local VI as VertexInfo_t
ConvertLimbToTriList(Object, Limb)
lock vertexdata for limb Object, Limb
` Create/recreate arrays needed for manipulation
undim Vertex()
global dim Vertex( get vertexdata vertex count() - 1 ) as VertexInfo_t
undim Index()
global dim Index( get vertexdata index count() - 1 ) as integer
` Copy all vertex data into an array
for i = 0 to array count( Vertex() )
V.x = get vertexdata position x(i)
V.y = get vertexdata position y(i)
V.z = get vertexdata position z(i)
V.u = get vertexdata u(i)
V.v = get vertexdata v(i)
V.Diffuse = get vertexdata diffuse(i)
Vertex(i).Vertex = V
Vertex(i).OriginalPosition = i
Vertex(i).Matches = i
Vertex(i).UsageCount = 0
next
` Copy all index data into an array
for i = 0 to array count( Index() )
Index(i) = get indexdata(i)
inc Vertex( Index(i) ).UsageCount
next
` Look for matching vertices
for i = 1 to array count( Vertex() )
if Vertex(i).UsageCount > 0
for j = 0 to i - 1
if CheckMatchingVertex(i, j) = 1
` Update the matching vertex
Vertex(j).UsageCount = Vertex(j).UsageCount + Vertex(i).UsageCount
` Update this duplicate vertex
Vertex(i).UsageCount = 0
Vertex(i).Matches = j
` Skip on to the next vertex
exit
endif
next
endif
next
` Sort the vertices so unused ones are at the end
repeat
Swap = 0
for i = 1 to array count( Vertex() )
if Vertex(i-1).UsageCount < Vertex(i).UsageCount
Swap = 1
VI = Vertex(i-1)
Vertex(i-1) = Vertex(i)
Vertex(i) = VI
endif
next
until Swap = 0
` Update the indices so that duplicates point at the original
for i = 0 to array count( Index() )
Index(i) = FindVertexFromIndex( Index(i) )
next
` Update the object index data
for i = 0 to array count( Index() )
set indexdata i, Index(i)
next
` Update the object vertex data
i = 0
while i <= array count( Vertex() )
if Vertex(i).UsageCount <= 0 then exit
set vertexdata position i, Vertex(i).Vertex.x, Vertex(i).Vertex.y, Vertex(i).Vertex.z
set vertexdata uv i, Vertex(i).Vertex.u, Vertex(i).Vertex.v
set vertexdata diffuse i, Vertex(i).Vertex.Diffuse
inc i
endwhile
` Remove unused vertices from the vertex data
delete mesh from vertexdata i, get vertexdata vertex count(), get vertexdata index count(), get vertexdata index count()
unlock vertexdata
GenerateNormalsForLimb(Object, Limb)
endfunction
function CheckMatchingVertex(i as integer, j as integer)
if Vertex(i).Vertex.x <> Vertex(j).Vertex.x then exitfunction 0
if Vertex(i).Vertex.y <> Vertex(j).Vertex.y then exitfunction 0
if Vertex(i).Vertex.z <> Vertex(j).Vertex.z then exitfunction 0
endfunction 1
function FindVertexFromIndex(Ind as integer)
for i = 0 to array count( Vertex() )
if Vertex(i).OriginalPosition = Ind
if Vertex(i).OriginalPosition = Vertex(i).Matches then exitfunction i
exitfunction FindVertexFromIndex( Vertex(i).Matches )
endif
next
endfunction -1
global DBProBasic3DDebug as integer
function LoadDLL_Basic3D()
for i = 1 to 256
if dll exist(i) = 0
DBProBasic3DDebug = i
load dll "DBProBasic3DDebug.dll", DBProBasic3DDebug
exitfunction
endif
next
DBProBasic3DDebug_DLL = -1
endfunction
function ConvertLimbToTriList(Object as integer, Limb as integer)
local Result as integer
local MeshAddr as dword
MeshAddr = GetMeshAddress(Object, Limb)
if MeshAddr
` If not a tri list, convert it
if PeekDword( MeshAddr + 36 ) <> 4
Result = call dll( DBProBasic3DDebug, "?ConvertLocalMeshToTriList@@YA_NPAUsMesh@@@Z", MeshAddr )
endif
` Really nasty ... if there is no index data, make some up!
if PeekDword( MeshAddr + 32 ) = 0
` Must lock the mesh before manipulating it, else changes won't happen
lock vertexdata for limb Object, Limb
` Get some memory for the index list
IndexData = make memory( get vertexdata vertex count() * 2 )
` Set the index count, and set it to point at the allocated memory
PokeDword( MeshAddr + 32, get vertexdata vertex count() )
PokeDword( MeshAddr + 24, IndexData )
` Put numbers into the index list
for i = 0 to get vertexdata vertex count()-1
set indexdata i, i
next
` Unlock to commit the changes
unlock vertexdata
endif
endif
endfunction Result
function GenerateNormalsForLimb(Object as integer, Limb as integer)
local Result as integer
local MeshAddr as dword
MeshAddr = GetMeshAddress(Object, Limb)
if MeshAddr
Result = call dll( DBProBasic3DDebug, "?GenerateNewNormalsForMesh@@YAXPAUsMesh@@@Z", MeshAddr )
endif
endfunction Result
function GetMeshAddress(Object as integer, Limb as integer)
local ObjectAddr as dword
local FrameListAddr as dword
local FrameAddr as dword
local MeshAddr as dword
if DBProBasic3DDebug = 0 then LoadDLL_Basic3D()
ObjectAddr = call dll( DBProBasic3DDebug, "?GetObjectA@@YAPAUsObject@@H@Z", Object )
if ObjectAddr
if PeekDword( ObjectAddr + 4 ) > Limb
FrameListAddr = PeekDword( ObjectAddr + 12 )
FrameAddr = PeekDword( FrameListAddr + (Limb * 4) )
MeshAddr = PeekDword( FrameAddr + 760 )
endif
endif
endfunction MeshAddr
function PeekDword(Addr as dword)
local Value as dword
Value = *Addr
endfunction Value
function PokeDword(Addr as dword, Value as dword)
*Addr = Value
endfunction
About the only thing that doesn't seem quite right is the normals recalculation - run it and you'll see a seam on the cylinder - this happens on most objects. It might end up being better if I did it myself.