Important information: this site is currently scheduled to go offline indefinitely by December 1st 2023.
If you wish to donate to attempt the preservation of tools and software somewhere else before it goes down, check the GoFundMe

How do normals work?

Post questions about game models here, or help out others!
Post Reply
Repeperilka
n00b
Posts: 14
Joined: Fri Jun 08, 2018 10:36 pm
Has thanked: 6 times
Been thanked: 1 time

How do normals work?

Post by Repeperilka »

I mean, I know they usualy weight 3 floats but I have no idea what's their purpose. Actualy I kind of know what they do, but not how they do it, and that's what I need to know. Like whats the purpose of each float that compose the normal.

I'm getting an error in noesis that looks like this:
WARNING: Size of normal list does not match positions, ignoring normals.
User avatar
Bigchillghost
double-veteran
double-veteran
Posts: 1031
Joined: Tue Jul 05, 2016 9:37 am
Has thanked: 32 times
Been thanked: 1216 times

Re: How do normals work?

Post by Bigchillghost »

Repeperilka wrote:I mean, I know they usualy weight 3 floats but I have no idea what's their purpose. Actualy I kind of know what they do, but not how they do it, and that's what I need to know. Like whats the purpose of each float that compose the normal.
Normals are unit vectors of which the square sum of the 3 coords equal to 1.0. And if you treat their coords as the ones of vertexes, you'll see them all distributing on the Unit Ball. Usually the normal vector of a vertex equals to the average of all the normal vectors of the faces this vert belongs to, but that's not always applicable for game models.
Repeperilka wrote:I'm getting an error in noesis that looks like this:
WARNING: Size of normal list does not match positions, ignoring normals.
Usually for game models the amount of vertexes of positions will equal to the ones of the normal vectors, and of the texcoords(UVs).
But you can optimize them by removing the copies of the same object, which'll result in different counts for positions/normals/UVs. Guess that's what your problem is about.
May you find peace in this puzzle-solving game. Say it with action: click the Image when you get helped.:)
Repeperilka
n00b
Posts: 14
Joined: Fri Jun 08, 2018 10:36 pm
Has thanked: 6 times
Been thanked: 1 time

Re: How do normals work?

Post by Repeperilka »

Bigchillghost wrote:Usually for game models the amount of vertexes of positions will equal to the ones of the normal vectors, and of the texcoords(UVs).
But you can optimize them by removing the copies of the same object, which'll result in different counts for positions/normals/UVs. Guess that's what your problem is about.
Hmm, it is probably a problem with my script because i've just made some debugging and this is what i am facing:

Code: Select all

Detected file type: Leisure Suit Larry
3
35
------Object14------
vertex count = 1668
face index count = 5325
normals count = 1668
uv's count = 1668
------Object16------
vertex count = 265
face index count = 666
normals count = 265
uv's count = 265
------Object17------
vertex count = 362
face index count = 777
normals count = 362
uv's count = 362
------Object28------
vertex count = 891
face index count = 2742
normals count = 891
uv's count = 891
------Object34------
vertex count = 274
face index count = 651
normals count = 274
uv's count = 274
WARNING: Size of normal list does not match positions, ignoring normals.
WARNING: Size of uv list does not match positions, ignoring uv's.
WARNING: Size of normal list does not match positions, ignoring normals.
WARNING: Size of uv list does not match positions, ignoring uv's.
WARNING: Size of normal list does not match positions, ignoring normals.
WARNING: Size of uv list does not match positions, ignoring uv's.
WARNING: Size of normal list does not match positions, ignoring normals.
WARNING: Size of uv list does not match positions, ignoring uv's.
WARNING: Size of normal list does not match positions, ignoring normals.
WARNING: Size of uv list does not match positions, ignoring uv's.
Generating normals for 1668 vertices...
Generating normals for 265 vertices...
Generating normals for 362 vertices...
Generating normals for 891 vertices...
Generating normals for 274 vertices...
Reading 'C:\Users\aleja\OneDrive\Escritorio\Larry Extract\out\GIRL\LARRYCOOL\\14'...Failed.
Reading 'C:\Users\aleja\OneDrive\Escritorio\Larry Extract\out\GIRL\LARRYCOOL\\16'...Failed.
Reading 'C:\Users\aleja\OneDrive\Escritorio\Larry Extract\out\GIRL\LARRYCOOL\\17'...Failed.
Reading 'C:\Users\aleja\OneDrive\Escritorio\Larry Extract\out\GIRL\LARRYCOOL\\28'...Failed.
Reading 'C:\Users\aleja\OneDrive\Escritorio\Larry Extract\out\GIRL\LARRYCOOL\\34'...Failed.
The uv's, normals and vertex arrays have the same length, and I've checked the uv's with ModelResearcher and they are good.
Bigchillghost wrote:Normals are unit vectors of which the square sum of the 3 coords equal to 1.0. And if you treat their coords as the ones of vertexes, you'll see them all distributing on the Unit Ball. Usually the normal vector of a vertex equals to the average of all the normal vectors of the faces this vert belongs to, but that's not always applicable for game models.
So, (vn1 + vn2 + vn3)^2 = 1.0? (I am not very good at math nor english)
User avatar
Bigchillghost
double-veteran
double-veteran
Posts: 1031
Joined: Tue Jul 05, 2016 9:37 am
Has thanked: 32 times
Been thanked: 1216 times

Re: How do normals work?

Post by Bigchillghost »

Repeperilka wrote:and I've checked the uv's with ModelResearcher and they are good.
Then you must have made some mistakes with your code. You'll need to post your script to reveal your issues.
Repeperilka wrote:So, (vn1 + vn2 + vn3)^2 = 1.0?
Nop. It's vnx^2 + vny^2 + vnz^2 = 1.0.
May you find peace in this puzzle-solving game. Say it with action: click the Image when you get helped.:)
Repeperilka
n00b
Posts: 14
Joined: Fri Jun 08, 2018 10:36 pm
Has thanked: 6 times
Been thanked: 1 time

Re: How do normals work?

Post by Repeperilka »

Code: Select all

from inc_noesis import *

def registerNoesisTypes():
	handle = noesis.register("Leisure Suit Larry", ".WGG")
	noesis.setHandlerTypeCheck(handle, noepyCheckType)
	noesis.setHandlerLoadModel(handle, noepyLoadModel)

	noesis.logPopup()
	return 1
	
def noepyCheckType(data):
	if len(data) < 8:
		return 0
	j = NoeBitStream(data)
	if j.readUInt() != 0x49535648:
		return 0
	return 1
	
def noepyLoadModel(data, mdlList):
	f = NoeBitStream(data)
	#start positions
	VertexStart = []
	f.seek(0x24, NOESEEK_ABS)
	TableStart = f.readUInt()
	NumberOfChunks = f.readUInt()
	StartChunkPos = f.readUInt() + 0x58
	f.seek(0x4, NOESEEK_REL)
	FacesStart = f.readUInt() + 0x8
	StartPosition = 0x58
	for i in range(0, NumberOfChunks):
		f.seek(StartChunkPos + 0x4, NOESEEK_ABS)
		vertCount = f.readUInt()
		f.seek(0x30, NOESEEK_REL)
		vertSize = f.readUInt()
		VertexStart.append(Chunk(vertSize, f.tell()))
		StartChunkPos = f.tell() + vertCount * vertSize
	print(len(VertexStart))
	
	#objects
	f.seek(StartPosition + TableStart + 0x8, NOESEEK_ABS)
	ObjectsCount = f.readUInt()
	f.seek(StartPosition, NOESEEK_ABS)
	Objects = []
	for i in range(0, ObjectsCount):
		Objects.append(f.readBytes(8).decode("ASCII").rstrip("\0"))
		f.seek(0x8, NOESEEK_REL)
	#table
	f.seek(StartPosition + TableStart, NOESEEK_ABS)
	NumberOfEntries = f.readUInt()
	f.seek(0x14, NOESEEK_REL)
	StartEntries = f.tell()
	Entries = []
	for i in range(0, NumberOfEntries):
		chunk = f.readUShort()
		f.seek(0xA, NOESEEK_REL)
		nVert = f.readUShort()
		f.seek(0x2, NOESEEK_REL)
		nFac = f.readUShort()
		f.seek(0x2, NOESEEK_REL)
		fac = f.readUShort()
		f.seek(0x4, NOESEEK_REL)
		vert = f.readUShort()
		f.seek(0x2, NOESEEK_REL)
		index = f.readUShort()
		f.seek(0x8, NOESEEK_REL)
		Entries.append(Entry(chunk, vert, fac, nVert, nFac, index))
	print(len(Entries))
	#objects
	Meshes = []
	FinalObjects = 0
	vertex = []
	faces = []
	normals = []
	uv = []
	for i in range(0, len(Entries)):
		LastVert = len(vertex)
		#Vertices
		pos = VertexStart[Entries[i].nChunk].startPosition + Entries[i].nVertex * VertexStart[Entries[i].nChunk].entrySize
		f.seek(pos, NOESEEK_ABS)
		if VertexStart[Entries[i].nChunk].entrySize == 40:
			for e in range (0, Entries[i].vertexCount):
				vertex.append (NoeVec3((f.readFloat(), f.readFloat(), f.readFloat())))
				f.seek(0x8, NOESEEK_REL)
				normals.append(NoeVec3((f.readFloat(), f.readFloat(), f.readFloat())))
				uv.append(NoeVec3((f.readFloat(), f.readFloat(), f.readFloat())))
				f.seek(-0x4, NOESEEK_REL)
		elif VertexStart[Entries[i].nChunk].entrySize == 36:
			for e in range (0, Entries[i].vertexCount):
				vertex.append (NoeVec3((f.readFloat(), f.readFloat(), f.readFloat())))
				f.seek(0x4, NOESEEK_REL)
				normals.append(NoeVec3((f.readFloat(), f.readFloat(), f.readFloat())))
				uv.append(NoeVec3((f.readFloat(), f.readFloat(), f.readFloat())))
				f.seek(-0x4, NOESEEK_REL)
		elif VertexStart[Entries[i].nChunk].entrySize == 32:
			for e in range (0, Entries[i].vertexCount):
				vertex.append (NoeVec3((f.readFloat(), f.readFloat(), f.readFloat())))
				normals.append(NoeVec3((f.readFloat(), f.readFloat(), f.readFloat())))
				uv.append(NoeVec3((f.readFloat(), f.readFloat(), f.readFloat())))
				f.seek(-0x4, NOESEEK_REL)
		#Faces
		pos = FacesStart + StartPosition + Entries[i].nFaces * 2
		f.seek(pos, NOESEEK_ABS)
		for e in range (0, Entries[i].facesCount):
			faces.append(f.readUShort() + LastVert)
		#setup1
		if (FinalObjects < Entries[i].objIndex):
			Meshes.append(NoeMesh (faces, vertex, str(i), str(i)))
			Meshes[FinalObjects].normals.append(normals)
			Meshes[FinalObjects].uvs.append(uv)
			print("------Object" + str(i) + "------")
			print("vertex count = " + str(len(vertex)))
			print ("face index count = " + str(len(faces)))
			print("normals count = " + str(len(normals)))
			print("uv's count = " + str(len(uv)))
			faces = []
			vertex = []
			normals = []
			uv = []
			FinalObjects = FinalObjects + 1
		
		#setup2
		#print(len(normals))
		#print(len(vertex))
		#Meshes.append(NoeMesh (faces, vertex, "Hola" + str(i), ""))
		#Meshes[i].normals.append(normals)
		#normals = []
		#vertex = []
		#faces = []
		#uv = []
	
	mdlList.append(NoeModel(Meshes, [], []))
	return 1
	
class Entry:
	nChunk = 0
	vertexCount = 0
	facesCount = 0
	nVertex = 0
	nFaces = 0
	objIndex = 0
	def __init__(self, chunk, vert, fac, nVert, nFac, index):
		self.nChunk = chunk
		self.vertexCount = vert
		self.facesCount = fac
		self.nVertex = nVert
		self.nFaces = nFac
		self.objIndex = index

class Chunk:
	startPosition = 0
	entrySize = 0
	def __init__(self, size, start):
		self.entrySize = size
		self.startPosition = start
This is a post i made because i did not know how was the information stored in the file, there is a link to the file for if you want to have it: viewtopic.php?f=10&t=19163
User avatar
Bigchillghost
double-veteran
double-veteran
Posts: 1031
Joined: Tue Jul 05, 2016 9:37 am
Has thanked: 32 times
Been thanked: 1216 times

Re: How do normals work?

Post by Bigchillghost »

Code: Select all

normals.append(NoeVec3((f.readFloat(), f.readFloat(), f.readFloat())))
It should be

Code: Select all

normals.append(NoeVec3([f.readFloat(), f.readFloat(), f.readFloat()]))
or simply use

Code: Select all

normals.append(NoeVec3.fromBytes(bs.readBytes(12)))

Code: Select all

uv.append(NoeVec3((f.readFloat(), f.readFloat(), f.readFloat())))
For most of the cases game models don't have the w component while there's no vec2 concept in Noesis, so you need to do it
like

Code: Select all

uv.append(NoeVec3([f.readFloat(), f.readFloat(), 0]))
May you find peace in this puzzle-solving game. Say it with action: click the Image when you get helped.:)
Repeperilka
n00b
Posts: 14
Joined: Fri Jun 08, 2018 10:36 pm
Has thanked: 6 times
Been thanked: 1 time

Re: How do normals work?

Post by Repeperilka »

I tried every combination possible with the things you told me, and i still get the same warning prompts.

Code: Select all

Detected file type: Leisure Suit Larry
3
45
------Object23------
vertex count = 2067
1st vert = (0.07027500867843628, 0.07789701223373413, 0.16757899522781372)
face index count = 7437
1st face = 0
normals count = 2067
1st normal = (0.6937000751495361, 0.6547720432281494, 0.300091028213501)
uv's count = 2067
1st uv = [0.4456320106983185, 0.38781505823135376, 0]
------Object28------
vertex count = 407
1st vert = (0.018630001693964005, 0.05230500549077988, 0.46636101603507996)
face index count = 1371
1st face = 0
normals count = 407
1st normal = (0.49452102184295654, 0.07861600816249847, 0.865602970123291)
uv's count = 407
1st uv = [0.4007430076599121, 0.04190700128674507, 0]
------Object34------
vertex count = 326
1st vert = (0.1076200008392334, -0.1154480054974556, 0.39223504066467285)
face index count = 1011
1st face = 0
normals count = 326
1st normal = (0.6704729795455933, -0.35633304715156555, 0.6507640480995178)
uv's count = 326
1st uv = [0.22328200936317444, 0.4489409923553467, 0]
------Object36------
vertex count = 150
1st vert = (0.02080800198018551, -0.10414400696754456, 0.46253401041030884)
face index count = 597
1st face = 0
normals count = 150
1st normal = (0.5784180164337158, -0.24716001749038696, 0.777396023273468)
uv's count = 150
1st uv = [0.4647040069103241, 0.04448600485920906, 0]
------Object39------
vertex count = 121
1st vert = (-0.08317900449037552, -0.051423002034425735, 0.12147300690412521)
face index count = 468
1st face = 0
normals count = 121
1st normal = (-0.7651110291481018, -0.15395501255989075, 0.6252229809761047)
uv's count = 121
1st uv = [0.9215329885482788, 0.7528210282325745, 0]
------Object42------
vertex count = 256
1st vert = (-0.0910470113158226, -0.02799900248646736, 0.08261200040578842)
face index count = 801
1st face = 0
normals count = 256
1st normal = (-0.9300490617752075, 0.08480000495910645, 0.35751602053642273)
uv's count = 256
1st uv = [0.9501460790634155, 0.07912901043891907, 0]
------Object44------
vertex count = 131
1st vert = (0.06489300727844238, -0.1863820105791092, -0.8974469900131226)
face index count = 213
1st face = 0
normals count = 131
1st normal = (0.12117300927639008, -0.6720520257949829, 0.730522096157074)
uv's count = 131
1st uv = [0.9698180556297302, 0.4650510251522064, 0]
WARNING: Size of normal list does not match positions, ignoring normals.
WARNING: Size of uv list does not match positions, ignoring uv's.
WARNING: Size of normal list does not match positions, ignoring normals.
WARNING: Size of uv list does not match positions, ignoring uv's.
WARNING: Size of normal list does not match positions, ignoring normals.
WARNING: Size of uv list does not match positions, ignoring uv's.
WARNING: Size of normal list does not match positions, ignoring normals.
WARNING: Size of uv list does not match positions, ignoring uv's.
WARNING: Size of normal list does not match positions, ignoring normals.
WARNING: Size of uv list does not match positions, ignoring uv's.
WARNING: Size of normal list does not match positions, ignoring normals.
WARNING: Size of uv list does not match positions, ignoring uv's.
WARNING: Size of normal list does not match positions, ignoring normals.
WARNING: Size of uv list does not match positions, ignoring uv's.
Generating normals for 2067 vertices...
Generating normals for 407 vertices...
Generating normals for 326 vertices...
Generating normals for 150 vertices...
Generating normals for 121 vertices...
Generating normals for 256 vertices...
Generating normals for 131 vertices...
Reading 'C:\Users\aleja\OneDrive\Escritorio\Larry Extract\out\GIRL\GIRLACTR\\23'...Failed.
Reading 'C:\Users\aleja\OneDrive\Escritorio\Larry Extract\out\GIRL\GIRLACTR\\28'...Failed.
Reading 'C:\Users\aleja\OneDrive\Escritorio\Larry Extract\out\GIRL\GIRLACTR\\34'...Failed.
Reading 'C:\Users\aleja\OneDrive\Escritorio\Larry Extract\out\GIRL\GIRLACTR\\36'...Failed.
Reading 'C:\Users\aleja\OneDrive\Escritorio\Larry Extract\out\GIRL\GIRLACTR\\39'...Failed.
Reading 'C:\Users\aleja\OneDrive\Escritorio\Larry Extract\out\GIRL\GIRLACTR\\42'...Failed.
Reading 'C:\Users\aleja\OneDrive\Escritorio\Larry Extract\out\GIRL\GIRLACTR\\44'...Failed.
It keep saying "Url of the archive...Failed" even though noesis is showing me the 3d model perfectly (with fucked up normals, of course)

Edit: i just aded a print with the first entry of every array
akderebur
double-veteran
double-veteran
Posts: 640
Joined: Fri Jul 08, 2011 10:36 am
Has thanked: 65 times
Been thanked: 898 times

Re: How do normals work?

Post by akderebur »

I am not really proficient in Python/Noesis but these lines probably insert the whole normal/uv array as a single object.

Code: Select all

Meshes[FinalObjects].normals.append(normals)
Meshes[FinalObjects].uvs.append(uv)
Try

Code: Select all

Meshes[FinalObjects].setNormals(normals)
Meshes[FinalObjects].setUVs(uv)
Repeperilka
n00b
Posts: 14
Joined: Fri Jun 08, 2018 10:36 pm
Has thanked: 6 times
Been thanked: 1 time

Re: How do normals work?

Post by Repeperilka »

It worked! I have to admit that i did not think this was going to work, but it did!

Thank you both, very much indeed!
Post Reply