SyntaxBomb - Indie Coders

Languages & Coding => BlitzMax / BlitzMax NG => MiniB3D => Topic started by: Kippykip on September 24, 2019, 19:35:03

Title: Some OpenB3D FPS Demo videos!
Post by: Kippykip on September 24, 2019, 19:35:03
Hey everyone, I thought it would be fun to share my little base FPS game project I'm working on using markcwm's OpenB3D (https://github.com/markcwm/openb3dmax.mod) module for BlitzMax-NG (https://github.com/bmx-ng/bmx-ng/releases).
I've designed it to look like an late 90s/early 2000s FPS game.
[yt_search]https://www.youtube.com/watch?v=xERADSYE900[/yt_search]
[yt_search]https://www.youtube.com/watch?v=sPv5fIJk9Xg[/yt_search]
Today I've been doing more work on these weapon scripts to add melee attack support. These scripts are .WPN files that kinda contain like an instruction set for weapons.
Just drag these .WPN files and the models/textures and whatever after the game has been compiled, and you can load them!
Although I seriously need to make a compiler for it, changing one minor thing means changing all the weapon state offsets again.
(https://i.imgur.com/HVbCBeW.png)
I ported the HL1 Crowbar over, have a look!
[yt_search]https://www.youtube.com/watch?v=uHtv7_gCdk4[/yt_search]
Anyway that's my little project, just trying to get groundwork done to make a full game of some kind. Right now almost all the resources are placeholders. But let me know what you think!
Title: Re: Some OpenB3D FPS Demo videos!
Post by: markcwm on September 26, 2019, 00:50:42
Hi KippyKip,

these are great proof-of-concept videos, I enjoy seeing where you take the engine.

Did you use LightMesh for vertex lighting? A very nice effect.
The scream NPC sound is clearly 3d, all sounds seem good.
The particle blood is very nice, did you use batch sprites or particle emitters?
The growing blood decals are a nice touch, they could maybe slow down with lifetime.
Also an impressive melee weapon.

These demos make me think I really need to add soft shadows next! Any suggestions?

PS: do you just use the CPP 3DS loader, have you tried the Blitzmax 3DS loader or does it not load your levels still?
Title: Re: Some OpenB3D FPS Demo videos!
Post by: 3DzForMe on September 26, 2019, 09:14:19
Very immersive,  love the screamers! The sound of the piano puts you nicely on edge....
Title: Re: Some OpenB3D FPS Demo videos!
Post by: Kippykip on September 26, 2019, 10:35:47
Quote from: markcwm on September 26, 2019, 00:50:42
Hi KippyKip,

these are great proof-of-concept videos, I enjoy seeing where you take the engine.

Did you use LightMesh for vertex lighting? A very nice effect.
The scream NPC sound is clearly 3d, all sounds seem good.
The particle blood is very nice, did you use batch sprites or particle emitters?
The growing blood decals are a nice touch, they could maybe slow down with lifetime.
Also an impressive melee weapon.

These demos make me think I really need to add soft shadows next! Any suggestions?

PS: do you just use the CPP 3DS loader, have you tried the Blitzmax 3DS loader or does it not load your levels still?

Thanks man!
And yeah I couldn't quite get LightMesh to look good, particularly with the Range (it appeared to just be dimming the whole thing instead?). So I actually wrote my own version of it. However it's a slow process so it's only done once on map.load or via map.refreshlights
So first of all I added a console command called Map.LoadRGB where you can define a default colour for everything. It works by using this ColorMesh function which goes through all the vertexes in the map and setting the colour to the RGB value.
Function ColorMesh(mesh:TMesh, Red:Float, Green:Float, Blue:Float, Alpha:Float = 1.0)
For Local TMP_X:Int = 1 To CountSurfaces(Mesh)
Local TMP_Surface:TSurface = GetSurface(Mesh, TMP_X)
For Local TMP_Y:Int = 0 To CountVertices(TMP_Surface) - 1
VertexColor(TMP_Surface, TMP_Y, Red, Red, Blue, Alpha)
Next
Next
End Function

Then I have these PolygonLight entities, which contain a Range, RGB value and a DetectWalls boolean.
All entities get their colours via a PolygonLight.TintEntity(TEntity) function, so when a entity is close to a PolygonLight entity (depending on the range set). It uses cycles through all the PolygonLight entities starting off with the default MapRGB colour, then adds the difference with the RGB values of the PolygonLights EntityColor. Then you just use EntityColor with the final result. That way it's fast to do in realtime, but if it's a big entity it won't look right.
Function TintEntity(Entity:TEntity, IncludeFlare = True, TMP_Red:Int = 255, TMP_Green:Int = 255, TMP_Blue:Int = 255)
Local RGB:Int[3]
If(IncludeFlare) 'Flare/glow from shooting a gun, or picking up an item etc.
RGB[0] = Map.MapRGB[0] + (TMP_Red - 255) + (HUD.FlareRGB[0] * HUD.FlareFade)
RGB[1] = Map.MapRGB[1] + (TMP_Green - 255) + (HUD.FlareRGB[1] * HUD.FlareFade)
RGB[2] = Map.MapRGB[2] + (TMP_Blue - 255) + (HUD.FlareRGB[2] * HUD.FlareFade)
Else
RGB[0] = Map.MapRGB[0] + (TMP_Red - 255)
RGB[1] = Map.MapRGB[1] + (TMP_Green - 255)
RGB[2] = Map.MapRGB[2] + (TMP_Blue - 255)
EndIf
'Cycle through all PolygonLight types
For Local Container:PolygonLight = EachIn PolygonLight.List
Local Distance:Float = EntityDistance(Entity, Container.Entity)
If(Distance < Container.Range)
Local EntityDifference:Float = 1 - (Distance / Container.Range)
RGB[0] = RGB[0] + (Container.RGB[0] * EntityDifference)
RGB[1] = RGB[1] + (Container.RGB[1] * EntityDifference)
RGB[2] = RGB[2] + (Container.RGB[2] * EntityDifference)
EndIf
Next
EntityColor(Entity, CapInt(RGB[0], 0, 255), CapInt(RGB[1], 0, 255), CapInt(RGB[2], 0, 255))
End Function


Now for maps of course this won't really cut it.
So instead imagine the above, but do it for every single vertex on the map. And if the DetectWalls boolean is enabled, then also check whether that vertex's view of the PolygonLight entity is blocked by other real nearby polygons in the map in between the light and the vertex.
So yeah doing this in realtime would be impossible ;).
Here's the full function if you want to have a look:
'WELL BOYS WE DID IT!
Function LightEntity(Entity:TEntity)
Local Mesh:TMesh = TMesh(Entity)

ColorMesh(Mesh, Map.MapRGB[0], Map.MapRGB[1], Map.MapRGB[2]) 'Give it default shading, generally 192 192 192 unless loaded with Map.LoadRGB instead.
'Repeat for each polygon light entity
For Local Container:PolygonLight = EachIn PolygonLight.List:TList
For Local TMP_X:Int = 1 To CountSurfaces(Mesh) 'Repeat this by the number of polys, for some reason this counts from 1
Local TMP_Surface:TSurface = GetSurface(Mesh, TMP_X) 'Get the polygon
For Local TMP_Y:Int = 0 To CountTriangles(TMP_Surface) - 1 'Repeat by the number of triangles (will probably always be 1 but just in case)
Local TMP_Triangle:Int[3]
TMP_Triangle[0] = TriangleVertex(TMP_Surface, TMP_Y, 0)
TMP_Triangle[1] = TriangleVertex(TMP_Surface, TMP_Y, 1)
TMP_Triangle[2] = TriangleVertex(TMP_Surface, TMP_Y, 2)

'Do for each vertex for that triangle
For Local TMP_Z = 0 To 2 '3 corners in a triangle ;^)
Local TMP_VertexX:Float = VertexX(TMP_Surface, TMP_Triangle[TMP_Z])
Local TMP_VertexY:Float = VertexY(TMP_Surface, TMP_Triangle[TMP_Z])
Local TMP_VertexZ:Float = VertexZ(TMP_Surface, TMP_Triangle[TMP_Z])
Local VertexDistance:Float = EntityDistanceXYZ(TMP_VertexX, TMP_VertexY, TMP_VertexZ, Container.Entity)

'Only do this when within range
If(VertexDistance < Container.Range)
'Do some math so the distance from one point to the entity is between 0.0-1.0
'Take from 1 so 1 = the closest and 0 = furthest
Local VertexDifference:Float = 1 - (VertexDistance / Container.Range)
'This part cuts off lighting if it's not in view
'Makes it insanely slow, but it only executes once anyway so eh
'If this sourcecode gets out and you dare to try this in real time, then
'END OF SLIDESHOW
'CLICK TO CONTINUE
If(Container.CheckWalls)
Local Shadow:Float = 1
'If that poly can't see the light entity because of another wall, don't light it
If(LineXYZ(TMP_VertexX, TMP_VertexY, TMP_VertexZ, Container.Entity)) Then Shadow = 0
'Colour all that... aww yeah
VertexColor(TMP_Surface, TMP_Triangle[TMP_Z], VertexRed(TMP_Surface, TMP_Triangle[TMP_Z]) + (Container.RGB[0] * VertexDifference * Shadow), VertexGreen(TMP_Surface, TMP_Triangle[TMP_Z]) + (Container.RGB[1] * VertexDifference * Shadow), VertexBlue(TMP_Surface, TMP_Triangle[TMP_Z]) + (Container.RGB[2] * VertexDifference * Shadow))
Else 'Otherwise just shade by nearest and don't check walls etc.
VertexColor(TMP_Surface, TMP_Triangle[TMP_Z], VertexRed(TMP_Surface, TMP_Triangle[TMP_Z]) + (Container.RGB[0] * VertexDifference), VertexGreen(TMP_Surface, TMP_Triangle[TMP_Z]) + (Container.RGB[1] * VertexDifference), VertexBlue(TMP_Surface, TMP_Triangle[TMP_Z]) + (Container.RGB[2] * VertexDifference))
EndIf
EndIf
Next
Next
Next
Next
End Function


Some of the things above also need these useful functions, like LineXYZ which checks if an entity can be seen or not depending if it's blocked by a wall:
'LineXYZ, coordinate version of the above
Function LineXYZ:Byte(Pos_X:Float, Pos_Y:Float, Pos_Z:Float, target:TEntity)
'Temporary make it pickable
EntityPickMode(target, 1)

'observer vector
TFormVector(0, 0, 1, Null, target)

'pick vector
Local dx:Float = (EntityX(target, True) - Pos_X)
Local dy:Float = (EntityY(target, True) - Pos_Y)
Local dz:Float = (EntityZ(target, True) - Pos_Z)

' check If something is blocking the view
If LinePick(Pos_X, Pos_Y, Pos_Z, dx, dy, dz, 0.01) = target
' observer can see target
EntityPickMode(target, 0)  'Unpickilate it
Return False
Else
' observer cannot see target
EntityPickMode(target, 0)  'Unpickilate it
Return True
EndIf
End Function

'Get distance between a coordinate and an entity
Function EntityDistanceXYZ:Float(TMP_X:Float, TMP_Y:Float, TMP_Z:Float, ent:TEntity)
Local xd:Float = EntityX(ent) - TMP_X
Local yd:Float = EntityY(ent) - TMP_Y
Local zd:Float = EntityZ(ent) - TMP_Z
Return KSqr(xd * xd + yd * yd + zd * zd)
End Function


All this adds about a second or two loading the Map, but you can get some really awesome results! And with a combination of SpriteLights, you can make a convincing room.
(Ignore the uncoloured gun, these screenshots were taken before I had made TintEntity)
(https://i.imgur.com/cOAF47y.png)
(https://i.imgur.com/THXtBsP.png)
(https://i.imgur.com/Q6cfnKh.png)
(https://i.imgur.com/YupvNMP.png)
(https://i.imgur.com/gfcmlpc.png)

As for the Decals/Particles, I'm just using a batch of TSprites. There is a decal limit where it begins to fade the decal entities when the count is greater than a variable (64 by default).
Also yeah I'm currently the CPP 3DS loader, although I haven't tested the regular loader in quite a while though.
Sadly I haven't got many suggestions other than maybe some more universal model formats. Although softshadows would be awesome! :)

Oh yeah and before I forget, I made some minor fixes to OpenB3D on github, such as making 3DS models with missing textures not crash and fullbright no longer gets affected by actual TLights like how it was in Blitz3D. Looking at the source, looks like the previous author disabled some lines of code and had them in the wrong order.
https://github.com/Kippykip/openb3dmax.mod/commits/master

Anyway hope these code snippets somewhat help in projects! I'm thinking about making an example demonstrating it fully.
Title: Re: Some OpenB3D FPS Demo videos!
Post by: Steve Elliott on September 26, 2019, 16:49:56
I like it.  Very surreal and atmospheric.   :D
Title: Re: Some OpenB3D FPS Demo videos!
Post by: Kippykip on September 28, 2019, 15:57:12
Quote from: Steve Elliott on September 26, 2019, 16:49:56
I like it.  Very surreal and atmospheric.   :D
Thanks! These are just testing grounds but I think some of these night tests would look good as a real level for a game. :))
Title: Re: Some OpenB3D FPS Demo videos!
Post by: markcwm on October 01, 2019, 00:08:06
Hi KippyKip,

yeah with LightMesh it's a bit tricky to get full illumination, the range is the distance between light and mesh and if it's too short the lighting dims, light_x/y/z is the light position, and just use EntityDistance(light,mesh) for the range. But since you've coded your own your obviously done with this.

Yes rather than add your code to the engine and cause a naming conflict with your project, it would probably be best to just add it all to an example. I can't actually follow your code, I can't find where you calculate the lighting, it should be the dot product of the light vector and mesh normal but you don't seem to be doing that. Anyway it looks like it works.

What sort of universal model formats would you want, ones with boned animation?

Yeah, I added your fixes to my branch, slightly different fix for 3DS but should work the same. The full-bright thing was because the RomanBath brass flame stands were not rendering right as there was no normal data, it was like looking at a mirror, but it seems they render right now so I've added full-bright back in.
Title: Re: Some OpenB3D FPS Demo videos!
Post by: RemiD on October 01, 2019, 07:10:11
@Kippykip>>cool atmosphere indeed  8) , but one thing is missing : raggdolls when you kill an ennemy ( imagine if you considered the direction + force of a hit to project an unconscious ennemy in the environment, and then let the physics handle the behavior of the body parts  ;D )
Title: Re: Some OpenB3D FPS Demo videos!
Post by: Kippykip on October 01, 2019, 13:12:02
Quote from: RemiD on October 01, 2019, 07:10:11
@Kippykip>>cool atmosphere indeed  8) , but one thing is missing : raggdolls when you kill an ennemy ( imagine if you considered the direction + force of a hit to project an unconscious ennemy in the environment, and then let the physics handle the behavior of the body parts  ;D )
Honestly I would love to have ragdoll physics, but I don't know the math required and I don't think it would fit a retro looking game. So I'll probably just stick with death/gib animations like Half-Life does or something. Right now these placeholder enemies just stop their animation and rotate 90 degrees :))

Quote from: markcwm on October 01, 2019, 00:08:06
Hi KippyKip,

yeah with LightMesh it's a bit tricky to get full illumination, the range is the distance between light and mesh and if it's too short the lighting dims, light_x/y/z is the light position, and just use EntityDistance(light,mesh) for the range. But since you've coded your own your obviously done with this.

Yes rather than add your code to the engine and cause a naming conflict with your project, it would probably be best to just add it all to an example. I can't actually follow your code, I can't find where you calculate the lighting, it should be the dot product of the light vector and mesh normal but you don't seem to be doing that. Anyway it looks like it works.

What sort of universal model formats would you want, ones with boned animation?

Yeah, I added your fixes to my branch, slightly different fix for 3DS but should work the same. The full-bright thing was because the RomanBath brass flame stands were not rendering right as there was no normal data, it was like looking at a mirror, but it seems they render right now so I've added full-bright back in.

Yeah I'll see if I can do a single .BMX file example sometime for this kind of lighting, as it's a bit confusing when the code is out of context.
Also awesome! I'll update OpenB3D and see if sketchup 3DS files work.
And yeah for models, something a bit more modern and more supported that allows boned animation would be awesome, so people can use something like readily available like Blender to create models if possible.