Jump to content

Tekken57's Maxscripts


tekken57

Recommended Posts

I've written a number of maxscripts which will come in handy for modders. Download the scripts here: https://mega.co.nz/#!A0d0AaqL!QDMnMVT33jQfyr92vhjneYOxF7sTa06R0jlcDyPznc4

Copy these scripts into your scripts directory and run them from 3dsmax. Read the instructions for each script below.

I am going to paste the scripts in plain text below should the link go down. If you want to use the plain text scripts below, copy the text into notepad and save it with an extension of ms. e.g. myscript.ms

 

-------------------------------------------------------------------------------------------------------------------------------------------------------------

 

Credit for the import script goes to Chrrox from Xentax, I edited his script for importing newer models and assigning weights for the pc version.

import_wwe_tekken : This script imports wwe 12 and upwards models. It assigns weights and normals to the pc models as well. Works for xbox 360, ps3 and pc models.  I have tested this script with 3dsmax 2015 without any issues. If you do not require weights, then run the script after this as it is slightly faster.

There are some models which give errors on this script, if you encounter an error, use the script without weights after this.

 

-- Script created by Chrrox from Xentax.com
-- Edited by tekken57 for importing of newer models
if (heapSize < 200000) then
        heapSize = 2000000 -- allow ~ 40 MB instead of just 7.5 MB. Prevents "Runtime Error: Out of scripter memory"
clearlistener()
fname = getOpenFileName \
caption:"WWE2012 upwards Model File" \
types:"WWEModel File(*.yobj)|*.yobj" \
historyCategory:"WWE Object Presets"
f = fopen fname "rb"

fn vToC valueVal = --accepts a point3 value, returns an equivalent color value
(
return [(valueVal.x*255 as integer), (valueVal.y*255 as integer), (valueVal.z*255 as integer)] as color
)
fn cToV colorVal = --accepts a color value, returns an equivalent point3 value
(
tempVal = colorVal as point3
return [(tempVal.x/255), (tempVal.y/255), (tempVal.z/255)]

)

fn RGBtoFPC clrRGB = (color (clrRGB.r/255) (clrRGB.g/255) (clrRGB.b/255))
fn FPCtoRGB clrFPC = (color (clrFPC.r*255) (clrFPC.g*255) (clrFPC.b*255))

fn ReadBEShort fstream = (
short = readshort fstream #unsigned
short = bit.swapBytes short 2 1
b = (bit.get short 16)
for i = 17 to 32 do short = bit.set short i b
return short
)

fn PrintOffset Var =
(
        local Var = Var
print ("This is the offset 0x" + (bit.intAsHex Var) as string)
        Var
)

fn floatSwap2 f =
(
        i = bit.floatAsInt f
        h = bit.intashex i
        while h.count < 8 do h = "0" + h
       
        s = (substring h 7 2) + (substring h 5 2) + (substring h 3 2) + (substring h 1 2)
        bit.intAsFloat (bit.hexasint s)
)       

fn ReadBEword fstream = (
return (bit.swapBytes (readshort fstream #unsigned) 1 2)
)

        fn convertTo32 input16 = (
                inputAsInt = input16
                sign = bit.get inputAsInt 16
                exponent = (bit.shift (bit.and inputAsInt (bit.hexasint "7C00")) -10) as integer - 16
                fraction = bit.and inputAsInt (bit.hexasint "03FF")
                if sign==true then sign = 1 else sign = 0
                exponentF = exponent + 127
                --Ouput 32 bit integer representing a 32 bit float
                outputAsFloat = bit.or (bit.or (bit.shift fraction 13) (bit.shift exponentF 23)) (bit.shift sign 31)
                --Output Check 
                return bit.intasfloat outputasfloat
        )

fn ReadBEHalfFloat fstream = (
return convertTo32(ReadBEword fstream)
)

fn ReadHalfFloat fstream = (
return convertTo32(readshort fstream#unsigned)
)

fn ReadBElong fstream = (
long = readlong fstream
long = bit.swapBytes long 1 4
long = bit.swapBytes long 2 3
return long
)

fn ReadBEfloat fstream = (
return floatSwap2(readfloat fstream)
)

fn ReadFixedString bstream fixedLen =
(
        local str = ""
        for i = 1 to fixedLen do
        (
                str += bit.intAsChar (ReadByte bstream #unsigned)
        )
        str
)

struct weight_data
(
   boneids,weights
)

Idstring = ReadFixedString f 4
Totalsize = ReadBElong f
Unk001 = ReadBElong f
Unk002 = ReadBElong f
Unk003 = ReadBElong f
Unk004 = ReadBElong f
MeshCount = ReadBElong f
MeshOff = ReadBElong f + 8
BoneCount = ReadBElong f
TexCount = ReadBElong f
BoneOffset = ReadBElong f + 8
TexOff = ReadBElong f + 8
MnameOff = ReadBElong f + 8
MnameCount = ReadBElong f
Unk013 = ReadBElong f
Unk014 = ReadBElong f
Unk015 = ReadBElong f
Unk016 = ReadBElong f
fseek f BoneOffset#seek_set
tarr = #()
rarr = #()
qarr = #()
Barr = #()
Parr = #()

for a = 1 to BoneCount Do (
BoneName = ReadFixedString f 16
tx = ReadBEfloat f
ty = ReadBEfloat f
tz = ReadBEfloat f
tw = ReadBEfloat f
rx = (ReadBEfloat f * 180) / pi
ry = (ReadBEfloat f * 180) / pi
rz = (ReadBEfloat f * 180) / pi
rw = ReadBEfloat f
parent = ReadBElong f
fseek f 0xC#seek_cur
qx = ReadBEfloat f
qy = ReadBEfloat f
qz = ReadBEfloat f
qw = ReadBEfloat f
tfm = (eulerangles rx ry rz) as matrix3
append tarr [tx,ty,tz]
append rarr tfm
append qarr [qx,qy,qz,qw]
append Barr BoneName
append Parr parent
)
BNArr = #()
for a = 1 to BoneCount Do (
if isvalidnode (getNodeByName Barr[a]) != true then (
tfm = rarr[a]
tfm.row4 = tarr[a]
if (Parr[a] != -1) do (
tfm = tfm * BNArr[(Parr[a] + 1)].objecttransform
)
newBone = bonesys.createbone   \
              tfm.row4   \
              (tfm.row4 + 0.01 * (normalize tfm.row1)) \
              (normalize tfm.row3)
         newBone.name = Barr[a]
         newBone.width  = 0.01
         newBone.height = 0.01
         newBone.transform = tfm
         newBone.setBoneEnable false 0
         newBone.wirecolor = yellow
         newbone.showlinks = true
         newBone.pos.controller      = TCB_position ()
         newBone.rotation.controller = TCB_rotation ()
              )
if isvalidnode (getNodeByName Barr[a]) == true then (
newBone = getNodeByName Barr[a]
)
if (Parr[a] != -1) then
newBone.parent = BNArr[(Parr[a] + 1)]
append BNArr newBone

)
format "BoneCount % \n" BoneCount
struct Mesh_Info_Struct
(
   Objname,VertOff,WeightOff,UVOff,MatPCount,matPOff,FaceOff,FaceSec,VertCount,WeightCount,BtableStart,  VertColorOff
)
fseek f MeshOff#seek_set
Mesh_Info_Arr = #()
for a = 1 to MeshCount Do (
VCount1 = ReadBElong f
FaceSec = ReadBElong f
BtableStart = (ftell f)
UsedBoneID = ReadBElong f
fseek f 0x50#seek_cur
WeightCount = ReadBElong f
Unk102 = ReadBElong f
Unk103 = ReadBElong f
VertOff = ReadBElong f + 8
WeightOff = ReadBElong f + 8
UVOff = ReadBElong f + 8
VertColorOff = ReadBElong f + 8
Unk107 = ReadBElong f
Objname = ReadFixedString f 16
Unk108 = ReadBElong f
Unk109 = ReadBElong f
MatPCount = ReadBElong f
matPOff = ReadBElong f + 8
FaceOff = ReadBElong f + 8
VertCount = ReadBElong f
Unk114 = ReadBElong f
fseek f 0x10#seek_cur
append Mesh_Info_Arr (Mesh_Info_Struct Objname:Objname VertOff:VertOff WeightOff:WeightOff UVOff:UVOff MatPCount:MatPCount matPOff:matPOff FaceOff:FaceOff FaceSec:FaceSec VertCount:VertCount WeightCount:WeightCount BtableStart:BtableStart  VertColorOff:VertColorOff)
)
--print Mesh_Info_Arr
for a = 1 to MeshCount Do (
--for a = 1 to 1 Do (
Vert_array = #()
Normal_array = #()
UV_array = #()
Face_array = #()
Weight_array = #()
UsedBonesPCArray = #()
VertexColor_array = #()

fseek f Mesh_Info_Arr[a].VertOff#seek_set
--scale1 = ReadBEfloat f
scale1 = 10
VertOff = ReadBElong f + 8
--print VertOff
fseek f VertOff#seek_set	
--print scale1
for b = 1 to Mesh_Info_Arr[a].VertCount Do (
vx = ReadBEfloat f
vy = ReadBEfloat f
vz = ReadBEfloat f
--fseek f 0x10#seek_cur
append Vert_array [vx,vy,vz]
nx = ReadBEfloat f  --read xyz coordinates
ny = ReadBEfloat f
nz = ReadBEfloat f
unk4444 = ReadBEfloat f
append Normal_array [nx,ny,nz]	
)

fseek f Mesh_Info_Arr[a].UVOff#seek_set
for b = 1 to Mesh_Info_Arr[a].VertCount Do (
tu = ReadBEfloat f
tv = ReadBEfloat f * -1
append UV_array [tu,tv,0]
)


--format  "vertcolor offset %\n" Mesh_Info_Arr[a].VertColorOff
fseek f Mesh_Info_Arr[a].VertColorOff#seek_set
for b = 1 to Mesh_Info_Arr[a].VertCount Do (
vcx = ReadBEfloat f
vcy = ReadBEfloat f 
vcz = ReadBEfloat f 	
append VertexColor_array  [vcx,vcy,vcz]
--format "vc % % %\n" vcx vcy vcz
)

fseek f Mesh_Info_Arr[a].WeightOff#seek_set
for b = 1 to Mesh_Info_Arr[a].VertCount Do (
Bone1 = -1
Bone2 = -1
Bone3 = -1
Bone4 = -1
Bone5 = -1
Bone6 = -1
Bone7 = -1
if Mesh_Info_Arr[a].WeightCount == 1 do (
Bone1 = readbyte f
fseek f 0x3#seek_cur
Weight1 = ReadBEfloat f
)
if Mesh_Info_Arr[a].WeightCount == 2 do (
Bone1 = readbyte f
fseek f 0x3#seek_cur
Weight1 = ReadBEfloat f
Bone2 = readbyte f
fseek f 0x3#seek_cur
Weight2 = ReadBEfloat f
)
if Mesh_Info_Arr[a].WeightCount == 3 do (
Bone1 = readbyte f
fseek f 0x3#seek_cur
Weight1 = ReadBEfloat f
Bone2 = readbyte f
fseek f 0x3#seek_cur
Weight2 = ReadBEfloat f
Bone3 = readbyte f
fseek f 0x3#seek_cur
Weight3 = ReadBEfloat f
)
if Mesh_Info_Arr[a].WeightCount == 4 do (
Bone1 = readbyte f
fseek f 0x3#seek_cur
Weight1 = ReadBEfloat f
Bone2 = readbyte f
fseek f 0x3#seek_cur
Weight2 = ReadBEfloat f
Bone3 = readbyte f
fseek f 0x3#seek_cur
Weight3 = ReadBEfloat f
Bone4 = readbyte f
fseek f 0x3#seek_cur
Weight4 = readfloat f   
)


if Mesh_Info_Arr[a].WeightCount == 5 do (
Bone1 = readbyte f
fseek f 0x3#seek_cur
Weight1 = ReadBEfloat f
Bone2 = readbyte f
fseek f 0x3#seek_cur
Weight2 = ReadBEfloat f
Bone3 = readbyte f
fseek f 0x3#seek_cur
Weight3 = ReadBEfloat f
Bone4 = readbyte f
fseek f 0x3#seek_cur
Weight4 = readfloat f 
Bone5 = readbyte f
fseek f 0x3#seek_cur
Weight5 = readfloat f 
   
)

if Mesh_Info_Arr[a].WeightCount == 6 do (
Bone1 = readbyte f
fseek f 0x3#seek_cur
Weight1 = ReadBEfloat f
Bone2 = readbyte f
fseek f 0x3#seek_cur
Weight2 = ReadBEfloat f
Bone3 = readbyte f
fseek f 0x3#seek_cur
Weight3 = ReadBEfloat f
Bone4 = readbyte f
fseek f 0x3#seek_cur
Weight4 = readfloat f 
Bone5 = readbyte f
fseek f 0x3#seek_cur
Weight5 = readfloat f  
Bone6 = readbyte f
fseek f 0x3#seek_cur
Weight6 = readfloat f     
)


if Mesh_Info_Arr[a].WeightCount == 7 do (
Bone1 = readbyte f
fseek f 0x3#seek_cur
Weight1 = ReadBEfloat f
Bone2 = readbyte f
fseek f 0x3#seek_cur
Weight2 = ReadBEfloat f
Bone3 = readbyte f
fseek f 0x3#seek_cur
Weight3 = ReadBEfloat f
Bone4 = readbyte f
fseek f 0x3#seek_cur
Weight4 = readfloat f 
Bone5 = readbyte f
fseek f 0x3#seek_cur
Weight5 = readfloat f  
Bone6 = readbyte f
fseek f 0x3#seek_cur
Weight6 = readfloat f   
Bone7 = readbyte f
fseek f 0x3#seek_cur
Weight7 = readfloat f   
)
w = (weight_data boneids:#() weights:#())
maxweight = 0
if(Bone1 != -1) then
   maxweight = maxweight + weight1
if(Bone2 != -1) then
   maxweight = maxweight + weight2
if(Bone3 != -1) then
   maxweight = maxweight + weight3
if(Bone4 != -1) then
   maxweight = maxweight + weight4
if(Bone5 != -1) then
   maxweight = maxweight + weight5
if(Bone6 != -1) then
   maxweight = maxweight + weight6
if(Bone7 != -1) then
   maxweight = maxweight + weight7
if(maxweight != 0) then (
      if(Bone1 > -1  ) then (
         w1 = weight1 as float
         append w.boneids (bone1 + 1)
         append w.weights w1
		 if (bone1 + 1 > -1) then
		(
		appendIfUnique UsedBonesPCArray (bone1 + 1)
		)
      )
      if(Bone2 > -1 ) then (
         w2 = weight2 as float
         append w.boneids (bone2 + 1)
         append w.weights w2
		if (bone2 + 1 > -1) then
		(  
	  appendIfUnique UsedBonesPCArray (bone2 + 1)
		) 
      )
      if(Bone3 > -1) then (
         w3 = weight3 as float
         append w.boneids (bone3 + 1)
         append w.weights w3
		   if (bone3 + 1 > -1) then
		(
	  appendIfUnique UsedBonesPCArray (bone3 + 1)
		) 
      )
      if(Bone4 > -1 ) then (
         w4 = weight4 as float
         append w.boneids (bone4 + 1)
         append w.weights w4
		if (bone4 + 1 > -1) then
		(   
	  appendIfUnique UsedBonesPCArray (bone4 + 1)
		) 
      )  
	if(Bone5 > -1)  then (
         w5 = weight5 as float
         append w.boneids (bone5 + 1)
         append w.weights w5
		 if (bone5 + 1 > -1) then
		(
		appendIfUnique UsedBonesPCArray (bone5 + 1)
		)
      )    
	if(Bone6 > -1 ) then (
         w6 = weight6 as float
         append w.boneids (bone6 + 1)
         append w.weights w6
		 if (bone6 + 1 > -1) then
		(
		appendIfUnique UsedBonesPCArray (bone6 + 1)
		)
      )    
	if(Bone7 > -1  ) then (
         w7 = weight7 as float
         append w.boneids (bone7 + 1)
         append w.weights w7
		if (bone7 + 1 > -1) then
		(
		appendIfUnique UsedBonesPCArray (bone7 + 1)
		)
		  
      )      
   )
append Weight_array w
)
fseek f Mesh_Info_Arr[a].FaceOff#seek_set
Ftmp = #()
for b = 1 to Mesh_Info_Arr[a].FaceSec Do (
FSize = ReadBElong f as float
FCount = ReadBElong f as float
FStart = (ReadBElong f + 8) as float
append Ftmp [FSize,FCount,FStart]
)
for b = 1 to Mesh_Info_Arr[a].FaceSec Do (
fseek f Ftmp[b].z#seek_set
FaceEnd = ((ftell f) + (2 * Ftmp[b].y))
StartDirection = 1
Face_array2 = #()
f1 = ReadBEword f + 1
f2 = ReadBEword f + 1
FaceDirection = StartDirection
do (
f3 = ReadBEword f
if (f3==0xFFFF) then (
f1 = ReadBEword f + 1
f2 = ReadBEword f + 1
FaceDirection = StartDirection   
) else (
f3 += 1
FaceDirection *= -1
if (f1!=f2)AND(f2!=f3)AND(f3!=f1) then (
append Face_array2 [(f1),(f2),(f3)]
if FaceDirection > 0 then append Face_array [(f1),(f2),(f3)]
else append Face_array [(f1),(f3),(f2)]
)
f1 = f2
f2 = f3
)
)while (ftell f) < (FaceEnd)
)
fseek f Mesh_Info_Arr[a].BtableStart#seek_set
usedID = ReadBElong f
Used_Bone_array = #()
for b = 1 to usedID do (
bid = ReadBElong f
append Used_Bone_array bid
)

msh = mesh vertices:Vert_array faces:Face_array  normals:Normal_array
msh.numTVerts = UV_array.count
buildTVFaces msh
--msh.name = Mesh_Name_array[a]
--msh.material = meditMaterials[1].materialList[(MatSlotTexID[a])]
for j = 1 to UV_array.count do setTVert msh j UV_array[j]
for j = 1 to Face_array.count do setTVFace msh j Face_array[j]
for j = 1 to Normal_array.count do setNormal msh j Normal_array[j]

setNumCPVVerts msh UV_array.count 
defaultVCFaces msh

for j = 1 to VertexColor_array.count do
(	
	vertColor = VertexColor_array[j] as point3
	convertedColor = vToC (vertColor) as color
	--convertedColor = vertColor as color
	format "convertedColor %\n" convertedColor
	setvertcolor msh j convertedColor
)

max modify mode
select msh
skinMod = skin ()
addModifier msh skinMod
print     "skinOps.getNumberVertices"
format "vCnt skinMod %\n" (skinOps.getNumberVertices skinMod)
if Used_Bone_array.count != 0 then
	(
		for i = 1 to Used_Bone_array.count do
		(
		   maxbone = BNArr[(Used_Bone_array[i])]
		   if i != Used_Bone_array.count then
			  skinOps.addBone skinMod maxbone 0
		   else
			  skinOps.addBone skinMod maxbone 1
		)
	)
else
	(
		for i = 1 to UsedBonesPCArray.count do
		(
			--format "bone array % \n" UsedBonesPCArray[i]
			--format "boneid % \n" UsedBonesPCArray[i]
		   --maxbone = BNArr[(UsedBonesPCArray[i])]
			maxbone = BNArr[(UsedBonesPCArray[i])]
		   if i != UsedBonesPCArray.count then
			  skinOps.addBone skinMod maxbone 0
		   else
			  skinOps.addBone skinMod maxbone 1
		)
	)
modPanel.setCurrentObject skinMod
print "weight array count"
print Weight_array.count
for i = 1 to Weight_array.count do
(
   w = Weight_array[i]
   bi = #()
   wv = #()	
   for j = 1 to w.boneids.count do
   (
	   --format "boneId % index % \n " w.boneids[j] (findItem UsedBonesPCArray w.boneids[j])
	   if Used_Bone_array.count != 0 then
	   (
		    boneid = w.boneids[j]
	   )
	   else
	   (
		    boneid = findItem UsedBonesPCArray w.boneids[j]
	   )
     
      weight = w.weights[j]
	 	   
      append bi boneid
      append wv weight
	--format "boneId % weight % \n "	boneid weight
   ) 

  skinOps.ReplaceVertexWeights skinMod i bi wv
)
max create mode
)

fclose f

 

 

-------------------------------------------------------------------------------------------------------------------------------------------------------------


import_wwe_tekken_no_weights: Same as above script but does not assign weights. Slight faster imports of models if you do not require weights.

 

-- Script created by Chrrox from Xentax.com
-- Edited by tekken57 for importing of newer models
if (heapSize < 200000) then
        heapSize = 2000000 -- allow ~ 40 MB instead of just 7.5 MB. Prevents "Runtime Error: Out of scripter memory"
clearlistener()
fname = getOpenFileName \
caption:"WWE2012 upwards Model File" \
types:"WWEModel File(*.yobj)|*.yobj" \
historyCategory:"WWE Object Presets"
f = fopen fname "rb"

fn vToC valueVal = --accepts a point3 value, returns an equivalent color value
(
return [(valueVal.x*255 as integer), (valueVal.y*255 as integer), (valueVal.z*255 as integer)] as color
)
fn cToV colorVal = --accepts a color value, returns an equivalent point3 value
(
tempVal = colorVal as point3
return [(tempVal.x/255), (tempVal.y/255), (tempVal.z/255)]

)

fn RGBtoFPC clrRGB = (color (clrRGB.r/255) (clrRGB.g/255) (clrRGB.b/255))
fn FPCtoRGB clrFPC = (color (clrFPC.r*255) (clrFPC.g*255) (clrFPC.b*255))

fn ReadBEShort fstream = (
short = readshort fstream #unsigned
short = bit.swapBytes short 2 1
b = (bit.get short 16)
for i = 17 to 32 do short = bit.set short i b
return short
)

fn PrintOffset Var =
(
        local Var = Var
print ("This is the offset 0x" + (bit.intAsHex Var) as string)
        Var
)

fn floatSwap2 f =
(
        i = bit.floatAsInt f
        h = bit.intashex i
        while h.count < 8 do h = "0" + h
       
        s = (substring h 7 2) + (substring h 5 2) + (substring h 3 2) + (substring h 1 2)
        bit.intAsFloat (bit.hexasint s)
)       

fn ReadBEword fstream = (
return (bit.swapBytes (readshort fstream #unsigned) 1 2)
)

        fn convertTo32 input16 = (
                inputAsInt = input16
                sign = bit.get inputAsInt 16
                exponent = (bit.shift (bit.and inputAsInt (bit.hexasint "7C00")) -10) as integer - 16
                fraction = bit.and inputAsInt (bit.hexasint "03FF")
                if sign==true then sign = 1 else sign = 0
                exponentF = exponent + 127
                --Ouput 32 bit integer representing a 32 bit float
                outputAsFloat = bit.or (bit.or (bit.shift fraction 13) (bit.shift exponentF 23)) (bit.shift sign 31)
                --Output Check 
                return bit.intasfloat outputasfloat
        )

fn ReadBEHalfFloat fstream = (
return convertTo32(ReadBEword fstream)
)

fn ReadHalfFloat fstream = (
return convertTo32(readshort fstream#unsigned)
)

fn ReadBElong fstream = (
long = readlong fstream
long = bit.swapBytes long 1 4
long = bit.swapBytes long 2 3
return long
)

fn ReadBEfloat fstream = (
return floatSwap2(readfloat fstream)
)

fn ReadFixedString bstream fixedLen =
(
        local str = ""
        for i = 1 to fixedLen do
        (
                str += bit.intAsChar (ReadByte bstream #unsigned)
        )
        str
)

struct weight_data
(
   boneids,weights
)

Idstring = ReadFixedString f 4
Totalsize = ReadBElong f
Unk001 = ReadBElong f
Unk002 = ReadBElong f
Unk003 = ReadBElong f
Unk004 = ReadBElong f
MeshCount = ReadBElong f
MeshOff = ReadBElong f + 8
BoneCount = ReadBElong f
TexCount = ReadBElong f
BoneOffset = ReadBElong f + 8
TexOff = ReadBElong f + 8
MnameOff = ReadBElong f + 8
MnameCount = ReadBElong f
Unk013 = ReadBElong f
Unk014 = ReadBElong f
Unk015 = ReadBElong f
Unk016 = ReadBElong f
fseek f BoneOffset#seek_set
tarr = #()
rarr = #()
qarr = #()
Barr = #()
Parr = #()

for a = 1 to BoneCount Do (
BoneName = ReadFixedString f 16
tx = ReadBEfloat f
ty = ReadBEfloat f
tz = ReadBEfloat f
tw = ReadBEfloat f
rx = (ReadBEfloat f * 180) / pi
ry = (ReadBEfloat f * 180) / pi
rz = (ReadBEfloat f * 180) / pi
rw = ReadBEfloat f
parent = ReadBElong f
fseek f 0xC#seek_cur
qx = ReadBEfloat f
qy = ReadBEfloat f
qz = ReadBEfloat f
qw = ReadBEfloat f
tfm = (eulerangles rx ry rz) as matrix3
append tarr [tx,ty,tz]
append rarr tfm
append qarr [qx,qy,qz,qw]
append Barr BoneName
append Parr parent
)
BNArr = #()
for a = 1 to BoneCount Do (
if isvalidnode (getNodeByName Barr[a]) != true then (
tfm = rarr[a]
tfm.row4 = tarr[a]
if (Parr[a] != -1) do (
tfm = tfm * BNArr[(Parr[a] + 1)].objecttransform
)
newBone = bonesys.createbone   \
              tfm.row4   \
              (tfm.row4 + 0.01 * (normalize tfm.row1)) \
              (normalize tfm.row3)
         newBone.name = Barr[a]
         newBone.width  = 0.01
         newBone.height = 0.01
         newBone.transform = tfm
         newBone.setBoneEnable false 0
         newBone.wirecolor = yellow
         newbone.showlinks = true
         newBone.pos.controller      = TCB_position ()
         newBone.rotation.controller = TCB_rotation ()
              )
if isvalidnode (getNodeByName Barr[a]) == true then (
newBone = getNodeByName Barr[a]
)
if (Parr[a] != -1) then
newBone.parent = BNArr[(Parr[a] + 1)]
append BNArr newBone

)
format "BoneCount % \n" BoneCount
struct Mesh_Info_Struct
(
   Objname,VertOff,WeightOff,UVOff,MatPCount,matPOff,FaceOff,FaceSec,VertCount,WeightCount,BtableStart,  VertColorOff
)
fseek f MeshOff#seek_set
Mesh_Info_Arr = #()
for a = 1 to MeshCount Do (
VCount1 = ReadBElong f
FaceSec = ReadBElong f
BtableStart = (ftell f)
UsedBoneID = ReadBElong f
fseek f 0x50#seek_cur
WeightCount = ReadBElong f
Unk102 = ReadBElong f
Unk103 = ReadBElong f
VertOff = ReadBElong f + 8
WeightOff = ReadBElong f + 8
UVOff = ReadBElong f + 8
VertColorOff = ReadBElong f + 8
Unk107 = ReadBElong f
Objname = ReadFixedString f 16
Unk108 = ReadBElong f
Unk109 = ReadBElong f
MatPCount = ReadBElong f
matPOff = ReadBElong f + 8
FaceOff = ReadBElong f + 8
VertCount = ReadBElong f
Unk114 = ReadBElong f
fseek f 0x10#seek_cur
append Mesh_Info_Arr (Mesh_Info_Struct Objname:Objname VertOff:VertOff WeightOff:WeightOff UVOff:UVOff MatPCount:MatPCount matPOff:matPOff FaceOff:FaceOff FaceSec:FaceSec VertCount:VertCount WeightCount:WeightCount BtableStart:BtableStart  VertColorOff:VertColorOff)
)
--print Mesh_Info_Arr
for a = 1 to MeshCount Do (
--for a = 1 to 1 Do (
Vert_array = #()
Normal_array = #()
UV_array = #()
Face_array = #()
Weight_array = #()
UsedBonesPCArray = #()
VertexColor_array = #()

fseek f Mesh_Info_Arr[a].VertOff#seek_set
--scale1 = ReadBEfloat f
scale1 = 10
VertOff = ReadBElong f + 8
--print VertOff
fseek f VertOff#seek_set	
--print scale1
for b = 1 to Mesh_Info_Arr[a].VertCount Do (
vx = ReadBEfloat f
vy = ReadBEfloat f
vz = ReadBEfloat f
--fseek f 0x10#seek_cur
append Vert_array [vx,vy,vz]
nx = ReadBEfloat f  --read xyz coordinates
ny = ReadBEfloat f
nz = ReadBEfloat f
unk4444 = ReadBEfloat f
append Normal_array [nx,ny,nz]	
)

fseek f Mesh_Info_Arr[a].UVOff#seek_set
for b = 1 to Mesh_Info_Arr[a].VertCount Do (
tu = ReadBEfloat f
tv = ReadBEfloat f * -1
append UV_array [tu,tv,0]
)


--format  "vertcolor offset %\n" Mesh_Info_Arr[a].VertColorOff
fseek f Mesh_Info_Arr[a].VertColorOff#seek_set
for b = 1 to Mesh_Info_Arr[a].VertCount Do (
vcx = ReadBEfloat f
vcy = ReadBEfloat f 
vcz = ReadBEfloat f 	
append VertexColor_array  [vcx,vcy,vcz]
--format "vc % % %\n" vcx vcy vcz
)

fseek f Mesh_Info_Arr[a].WeightOff#seek_set
for b = 1 to Mesh_Info_Arr[a].VertCount Do (
Bone1 = -1
Bone2 = -1
Bone3 = -1
Bone4 = -1
Bone5 = -1
Bone6 = -1
Bone7 = -1
if Mesh_Info_Arr[a].WeightCount == 1 do (
Bone1 = readbyte f
fseek f 0x3#seek_cur
Weight1 = ReadBEfloat f
)
if Mesh_Info_Arr[a].WeightCount == 2 do (
Bone1 = readbyte f
fseek f 0x3#seek_cur
Weight1 = ReadBEfloat f
Bone2 = readbyte f
fseek f 0x3#seek_cur
Weight2 = ReadBEfloat f
)
if Mesh_Info_Arr[a].WeightCount == 3 do (
Bone1 = readbyte f
fseek f 0x3#seek_cur
Weight1 = ReadBEfloat f
Bone2 = readbyte f
fseek f 0x3#seek_cur
Weight2 = ReadBEfloat f
Bone3 = readbyte f
fseek f 0x3#seek_cur
Weight3 = ReadBEfloat f
)
if Mesh_Info_Arr[a].WeightCount == 4 do (
Bone1 = readbyte f
fseek f 0x3#seek_cur
Weight1 = ReadBEfloat f
Bone2 = readbyte f
fseek f 0x3#seek_cur
Weight2 = ReadBEfloat f
Bone3 = readbyte f
fseek f 0x3#seek_cur
Weight3 = ReadBEfloat f
Bone4 = readbyte f
fseek f 0x3#seek_cur
Weight4 = readfloat f   
)


if Mesh_Info_Arr[a].WeightCount == 5 do (
Bone1 = readbyte f
fseek f 0x3#seek_cur
Weight1 = ReadBEfloat f
Bone2 = readbyte f
fseek f 0x3#seek_cur
Weight2 = ReadBEfloat f
Bone3 = readbyte f
fseek f 0x3#seek_cur
Weight3 = ReadBEfloat f
Bone4 = readbyte f
fseek f 0x3#seek_cur
Weight4 = readfloat f 
Bone5 = readbyte f
fseek f 0x3#seek_cur
Weight5 = readfloat f 
   
)

if Mesh_Info_Arr[a].WeightCount == 6 do (
Bone1 = readbyte f
fseek f 0x3#seek_cur
Weight1 = ReadBEfloat f
Bone2 = readbyte f
fseek f 0x3#seek_cur
Weight2 = ReadBEfloat f
Bone3 = readbyte f
fseek f 0x3#seek_cur
Weight3 = ReadBEfloat f
Bone4 = readbyte f
fseek f 0x3#seek_cur
Weight4 = readfloat f 
Bone5 = readbyte f
fseek f 0x3#seek_cur
Weight5 = readfloat f  
Bone6 = readbyte f
fseek f 0x3#seek_cur
Weight6 = readfloat f     
)


if Mesh_Info_Arr[a].WeightCount == 7 do (
Bone1 = readbyte f
fseek f 0x3#seek_cur
Weight1 = ReadBEfloat f
Bone2 = readbyte f
fseek f 0x3#seek_cur
Weight2 = ReadBEfloat f
Bone3 = readbyte f
fseek f 0x3#seek_cur
Weight3 = ReadBEfloat f
Bone4 = readbyte f
fseek f 0x3#seek_cur
Weight4 = readfloat f 
Bone5 = readbyte f
fseek f 0x3#seek_cur
Weight5 = readfloat f  
Bone6 = readbyte f
fseek f 0x3#seek_cur
Weight6 = readfloat f   
Bone7 = readbyte f
fseek f 0x3#seek_cur
Weight7 = readfloat f   
)
w = (weight_data boneids:#() weights:#())
maxweight = 0
if(Bone1 != -1) then
   maxweight = maxweight + weight1
if(Bone2 != -1) then
   maxweight = maxweight + weight2
if(Bone3 != -1) then
   maxweight = maxweight + weight3
if(Bone4 != -1) then
   maxweight = maxweight + weight4
if(Bone5 != -1) then
   maxweight = maxweight + weight5
if(Bone6 != -1) then
   maxweight = maxweight + weight6
if(Bone7 != -1) then
   maxweight = maxweight + weight7
if(maxweight != 0) then (
      if(Bone1 > -1  ) then (
         w1 = weight1 as float
         append w.boneids (bone1 + 1)
         append w.weights w1
		 if (bone1 + 1 > -1) then
		(
		appendIfUnique UsedBonesPCArray (bone1 + 1)
		)
      )
      if(Bone2 > -1 ) then (
         w2 = weight2 as float
         append w.boneids (bone2 + 1)
         append w.weights w2
		if (bone2 + 1 > -1) then
		(  
	  appendIfUnique UsedBonesPCArray (bone2 + 1)
		) 
      )
      if(Bone3 > -1) then (
         w3 = weight3 as float
         append w.boneids (bone3 + 1)
         append w.weights w3
		   if (bone3 + 1 > -1) then
		(
	  appendIfUnique UsedBonesPCArray (bone3 + 1)
		) 
      )
      if(Bone4 > -1 ) then (
         w4 = weight4 as float
         append w.boneids (bone4 + 1)
         append w.weights w4
		if (bone4 + 1 > -1) then
		(   
	  appendIfUnique UsedBonesPCArray (bone4 + 1)
		) 
      )  
	if(Bone5 > -1)  then (
         w5 = weight5 as float
         append w.boneids (bone5 + 1)
         append w.weights w5
		 if (bone5 + 1 > -1) then
		(
		appendIfUnique UsedBonesPCArray (bone5 + 1)
		)
      )    
	if(Bone6 > -1 ) then (
         w6 = weight6 as float
         append w.boneids (bone6 + 1)
         append w.weights w6
		 if (bone6 + 1 > -1) then
		(
		appendIfUnique UsedBonesPCArray (bone6 + 1)
		)
      )    
	if(Bone7 > -1  ) then (
         w7 = weight7 as float
         append w.boneids (bone7 + 1)
         append w.weights w7
		if (bone7 + 1 > -1) then
		(
		appendIfUnique UsedBonesPCArray (bone7 + 1)
		)
		  
      )      
   )
append Weight_array w
)
fseek f Mesh_Info_Arr[a].FaceOff#seek_set
Ftmp = #()
for b = 1 to Mesh_Info_Arr[a].FaceSec Do (
FSize = ReadBElong f as float
FCount = ReadBElong f as float
FStart = (ReadBElong f + 8) as float
append Ftmp [FSize,FCount,FStart]
)
for b = 1 to Mesh_Info_Arr[a].FaceSec Do (
fseek f Ftmp[b].z#seek_set
FaceEnd = ((ftell f) + (2 * Ftmp[b].y))
StartDirection = 1
Face_array2 = #()
f1 = ReadBEword f + 1
f2 = ReadBEword f + 1
FaceDirection = StartDirection
do (
f3 = ReadBEword f
if (f3==0xFFFF) then (
f1 = ReadBEword f + 1
f2 = ReadBEword f + 1
FaceDirection = StartDirection   
) else (
f3 += 1
FaceDirection *= -1
if (f1!=f2)AND(f2!=f3)AND(f3!=f1) then (
append Face_array2 [(f1),(f2),(f3)]
if FaceDirection > 0 then append Face_array [(f1),(f2),(f3)]
else append Face_array [(f1),(f3),(f2)]
)
f1 = f2
f2 = f3
)
)while (ftell f) < (FaceEnd)
)
fseek f Mesh_Info_Arr[a].BtableStart#seek_set
usedID = ReadBElong f
Used_Bone_array = #()
for b = 1 to usedID do (
bid = ReadBElong f
append Used_Bone_array bid
)

msh = mesh vertices:Vert_array faces:Face_array  normals:Normal_array
msh.numTVerts = UV_array.count
buildTVFaces msh
--msh.name = Mesh_Name_array[a]
--msh.material = meditMaterials[1].materialList[(MatSlotTexID[a])]
for j = 1 to UV_array.count do setTVert msh j UV_array[j]
for j = 1 to Face_array.count do setTVFace msh j Face_array[j]
for j = 1 to Normal_array.count do setNormal msh j Normal_array[j]

setNumCPVVerts msh UV_array.count 
defaultVCFaces msh

for j = 1 to VertexColor_array.count do
(	
	vertColor = VertexColor_array[j] as point3
	convertedColor = vToC (vertColor) as color
	--convertedColor = vertColor as color
	format "convertedColor %\n" convertedColor
	setvertcolor msh j convertedColor
)

max modify mode
select msh
skinMod = skin ()
addModifier msh skinMod
print     "skinOps.getNumberVertices"
format "vCnt skinMod %\n" (skinOps.getNumberVertices skinMod)
if Used_Bone_array.count != 0 then
	(
		for i = 1 to Used_Bone_array.count do
		(
		   maxbone = BNArr[(Used_Bone_array[i])]
		   if i != Used_Bone_array.count then
			  skinOps.addBone skinMod maxbone 0
		   else
			  skinOps.addBone skinMod maxbone 1
		)
	)
else
	(
		for i = 1 to UsedBonesPCArray.count do
		(
			--format "bone array % \n" UsedBonesPCArray[i]
			--format "boneid % \n" UsedBonesPCArray[i]
		   --maxbone = BNArr[(UsedBonesPCArray[i])]
			maxbone = BNArr[(UsedBonesPCArray[i])]
		   if i != UsedBonesPCArray.count then
			  skinOps.addBone skinMod maxbone 0
		   else
			  skinOps.addBone skinMod maxbone 1
		)
	)
modPanel.setCurrentObject skinMod
print "weight array count"
print Weight_array.count
for i = 1 to Weight_array.count do
(
   w = Weight_array[i]
   bi = #()
   wv = #()	
   for j = 1 to w.boneids.count do
   (
	   --format "boneId % index % \n " w.boneids[j] (findItem UsedBonesPCArray w.boneids[j])
	   if Used_Bone_array.count != 0 then
	   (
		    boneid = w.boneids[j]
	   )
	   else
	   (
		    boneid = findItem UsedBonesPCArray w.boneids[j]
	   )
     
      weight = w.weights[j]
	 	   
      append bi boneid
      append wv weight
	--format "boneId % weight % \n "	boneid weight
   ) 

  --skinOps.ReplaceVertexWeights skinMod i bi wv
)
max create mode
)

fclose f

 

 

-------------------------------------------------------------------------------------------------------------------------------------------------------------

 

tekken_export_wavefront:  This script exports an object to a wavefront object with vertex normal data. Select your object in 3dsmax and use this script to export when you want to import normal data into your yobj file. The normal import is not coded into X-Rey as yet and will be included in the next version. When the new version of X-Rey is released, import as currently do so.

 

 

-- Coded by tekken57 

global counter = 2
--saveDir = getSavePath caption:"Save Directory" initialDir:"$scripts"

curObject  = $
objName = $.name + "_normals.obj"
--print (saveDir  + objName)
--out_name = ("c:/weightdata.txt")
saveDir = GetSaveFileName filename:objName types:"Wavefont Object file (*.obj)|*.obj|All Files(*.*)|*.*|"


out_name = saveDir --+ @"\" + objName 
print out_name
global out_file = createfile out_name
global UVFormat = "vt % % 0\n"

fn PrintPoint pt = (
		format "v % % %\n" pt.x (pt.z ) (pt.y * -1) to:out_file
	) 	
	
	fn PrintPointNormal pt = (
		format "vn % % %\n" (pt.x * -1) (pt.z) pt.y to:out_file
	)

	fn PrintPointUV pt = (
		format "vt % % 0\n" pt.x pt.y to:out_file
	) 	

	fn PrintPointInt pt = (
		x = (int(pt.x) - 1) + 1
		y = (int(pt.y) - 1) + 1
		z = (int(pt.z) - 1) + 1
		format "f %/% %/% %/% \n" x x y y z z to:out_file
	) 	

	function ExtractUvs obj whereto =
	(
		n = obj.numTVerts
		
		for i = 1 to n do 
		(

			v = GetTVert obj i
			append whereto v

		)

	)

	function DumpUvs src = 
	(

		--Format "\"uvs\": [[" to:out_file

		num = src.count

		if num > 0 then
		(

			for i = 1 to num do
			(

				uvw = src[i]

				u = uvw.x

				
					v = uvw.y
				

				Format UVFormat u v to:out_file			

			)

		)

		

	)
	
	
	function ExtractColors obj whereto =
	(

		nColors = GetNumCPVVerts obj
		
		if nColors > 0 then
		(

			for i = 1 to nColors do
			(

				c = GetVertColor obj i
				append whereto c

			)

		)

	)

	
	function DumpColors src useColors = 
	(

		

		num = src.count

		if num > 0 and useColors then
		(

			for i = 1 to num do
			(

				col = src[i]
				--format "vs % \n" col.x  to:out_file			
				r = col.r as Float
				g = col.g as float
				b = col.b as float
				
				--r = r/255
				--g = g/255
				--b= b/255
				
				hexNum = ( bit.shift r 16 ) + ( bit.shift g 8 ) + b
				
				--hexColor = formattedPrint hexNum format:"#x"
				-- Format "%" hexColor to:ostream

				--decColor = formattedPrint hexNum format:"#d"
				--Format "% \n" decColor to:out_file		
				--Format "vs % % %\n"	 r g b to:out_file		
				Format "vs % \n"	 hexNum to:out_file		
				
					

			)

		)

		

	)	

for j in selection do
(

local sel = convertToMesh j
	--print (sel)	
	
case classOf sel of
(
editable_poly: polyOp.setFaceMatID sel (polyOp.getFaceSelection sel) counter
Editable_Mesh: 
	(
		
		
		local face_selection = #{}
		local base_obj = sel
		local num_faces = getNumFaces base_obj f
		num_verts = j.numverts 
		
			-- Vertex Positions 
			
			for i = 1 to num_verts do
			(
				vert = getVert j i
				PrintPoint vert
			)
			
			--normals
			for i = 1 to num_verts do
			(
				vert = getNormal j i				
				PrintPointNormal vert
				
			)
			
			-- Vertex Texture Coordinates 
		
			/*
			for i = 1 to num_faces do
			(
				-- Iterate over faces 				
				tvface = getTVFace j i
				for k = 1 to 3 do (
					--if (k > 1) then format ", " to:out_file
					-- Get a specific texture vertex
					tvert = getTVert j tvface[k]		
					PrintPointUV tvert
				)
			)
			*/
			mergedUvs = #()
			ExtractUvs j mergedUvs
			DumpUvs mergedUvs
			
			
			
			-- get vertex colors
				/*	
			if getNumCPVVerts j > 0 then
			(
			mergedColors = #()
			ExtractColors j mergedColors
			DumpColors mergedColors true
			)
			
			*/
			--get faces 
			
			format "g Object0 \n" to:out_file
			format "s 1 \n" to:out_file
			
			--format "    \"indices\" : [" to:out_file
			for i = 1 to num_faces do
			(
				--if (i > 1) then format ", " to:out_file
				face = getFace j i
				PrintPointInt face
			)
			--format "]\n" to:out_file

			


	
)
)
counter = counter + 1
--print (counter)
--print (classOf sel)

)

close out_file
--edit out_name
messagebox "Object exported to path selected. http://sf4mods.blogspot.com"



 

 

-------------------------------------------------------------------------------------------------------------------------------------------------------------

 

export_bone_weights_to_file: This exports the vertex weights of an object into a text file which can be imported into the forthcoming version of X-Rey. Select the skin modifier of the object and then run the script. To import into the  forthcoming version of X-Rey, click the weights button.

 

-- coded by tekken57

(
clearlistener()


curObject  = $
objName = $.name + ".txt"
--print (saveDir  + objName)
--out_name = ("c:/weightdata.txt")
saveDir = GetSaveFileName filename:objName types:"Text file (*.txt)|*.txt|All Files(*.*)|*.*|"

	
--saveDir = getSavePath caption:"Save Directory" initialDir:"$scripts"
	
--curObject  = $
--objName = $.name
--print (saveDir  + objName)
--out_name = ("c:/weightdata.txt")
--out_name = saveDir + objName + ".txt"
	out_name = saveDir
out_file = createfile out_name
skinMod = $.modifiers["skin"]
for i = 1 to (skinOps.GetNumberVertices skinMod) do
(
--format ("Vertex % \n")i
for j = 1 to (skinOps.GetVertexWeightCount skinMod i) do
(
boneID = (skinOps.GetVertexWeightBoneID skinMod i j) 
boneName = (skinOps.GetBoneName skinMod boneID 0)
VertWeight = (skinOps.GetVertexWeight skinMod i j)
format ("% % ")(boneID - 1) VertWeight to:out_file
)
format("\n") to:out_file
)
close out_file
--edit out_name
messagebox "Weights exported to path selected"
)

 

-------------------------------------------------------------------------------------------------------------------------------------------------------------

assign_id_complete2: This script assigns a material ID to all selected objects. Ensure that you do not have a material (texture) assigned to the object before running this script.

-- coded by tekken57
global counter = 2
for j in selection do
(

local sel = convertToMesh j
	--print (sel)	
case classOf sel of
(
editable_poly: polyOp.setFaceMatID sel (polyOp.getFaceSelection sel) counter
Editable_Mesh: 
	(
		
		
		local face_selection = #{}
		local base_obj = sel
		local num_faces = getNumFaces base_obj f
		
		for f = 1 to num_faces do
			(
				 face_selection[f] = true
	
			)--end f loop
			setFaceSelection sel face_selection
			
	
	for i in (getFaceSelection sel) do 
	(
		setFaceMatID sel i counter
		--print sel
		--print i 
	)
	
)
)
counter = counter + 1
--print (counter)
--print (classOf sel)

)
messagebox "Material Id's assigned to meshes. http://sf4mods.blogspot.com"


-------------------------------------------------------------------------------------------------------------------------------------------------------------

attach_meshes2: This script attaches all selected meshes. If you intend to detach the meshes, assign an id using the previous script. Ensure that your objects do not have a material assigned before running this script as the objects will lost the material ID when attached.

 

-- coded by tekken57
undo off
(
hideByCategory.bones = true
while selection.count > 1 do
(
selcount = selection.count
for i = selcount to 2 by -2 do
(
meshop.attach selection[i] selection[i-1]
)
)
update selection[1]
)

 

-------------------------------------------------------------------------------------------------------------------------------------------------------------

Edited by tekken57
  • Like 2
Link to comment
Share on other sites

Very cool. Thanks for sharing, man! May I ask what are the advantages to using these? I've seen you mention the weight thing when it comes to models plenty of times in the past--and how without them assigned, you've ran into issues with things--but what exactly do they do that wouldn't have been able to be done without them?

Link to comment
Share on other sites

Got interrupted while posting the scripts, added the rest of the scripts to the main post.

 

@OTE, the import scripts can be used with newer versions of 3dsmax and import the weights correctly. Breinj's script doesn't seem to import the weights but his script does do vertex colors which mine doesn't do. The import scripts work a bit faster so the importing of pc models don't take as long.

 

I'm not an expert on 3d modelling but from what I understand, the object defines what bones affect the movement of the object. The weight defines how much of an impact the bone movement makes on the object. I've been looking at importing this data as a solution to the face corruption issues on the pc models when facial animations are enabled but it doesn't seem to sort the issue out. I'm posting the script and coding an import for the weights irrespective as maybe someone else can figure the issue out. the weights may also be useful for the 360 models.

 

The export function exports in standard wavefront object but also includes the vertex normal data which the 3dsmax exporter does not include. The vertex normal data makes a big difference when it comes to getting your modded wrestlers face looking close to your 3dsmax preview.

Edited by tekken57
Link to comment
Share on other sites

 Thanks for the explanation, man--much appreciated!

These will come in very handy, there is no shortage to your talents sir!

Ain't that the truth.

Edited by OverTheEdge
Link to comment
Share on other sites

...So with the export weights script can we have animated faces again without the distortion?

 

That was the plan but the weights hasn't fixed that issue. You guys can mess with it as I don't know how to recalculate the weights in 3dsmax, maybe you will have more luck.

Link to comment
Share on other sites

  • tekken57 pinned this topic
  • 5 years later...

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.   Restore formatting

  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.

×
×
  • Create New...