#### Thunder Chicken

Donator
This is froma similar object (ignore the flag), the UVs look different?
Code:
LABEL huddisplay
MATERIAL 11
TEXTURE 0
FLAG 7
GEOM 4 2
-0.0750 0.8638 2.6239 0.0000 0.0000 -1.0000 0.0000 0.0000
0.0750 0.8638 2.6239 0.0000 0.0000 -1.0000 1.0000 0.0000
0.0750 0.7138 2.6239 0.0000 0.0000 -1.0000 1.0000 1.0000
-0.0750 0.7138 2.6239 0.0000 0.0000 -1.0000 -0.0000 1.0000
2 3 0
1 2 0
I think I see what is going on. The UV coordinates provide the information needed to orient the texture properly. Stand by...

#### Thunder Chicken

Donator
OK, some progress:

The -Z face normals, needed to render the panel opaque, reverses the orientation of the UV mapping coordinates. (EDIT No, they don't) Once I figured that out (EDIT I didn't) it was easy enough the establish the two lower corners, but I have to do some more mathing to straighten out the upper corners. I think I'll get there.

Got to make sure to make even numbers of sign errors at all times.

Last edited:

#### Thunder Chicken

Donator
That was a mission.

Here is my group:

Code:
LABEL Instrument_Panel
MATERIAL 0
TEXTURE 1
GEOM 4 2
-0.358 0.102 0.895 0 0 -1 0 1
0.358 0.102 0.895 0 0 -1 1 1
0.102 0.256 0.895 0 0 -1 0.6425 0
-0.102 0.256 0.895 0 0 -1 0.3575 0
0 3 1
3 2 1

I initially thought that the normal direction affected this mapping, but it doesn't.

I got really confused because this is how texture mapping is described in the 3DModel.pdf:

3DModel.pdf said:
Texture coordinates define how a rectangular 2D texture is mapped onto the object surface. Texture coordinate (0,0) refers to the lower left corner of the texture, (1,1) refers to the upper right corner.

That doesn't seem to be correct because if it were, the UV coordinates for the above vertices would have been:

0, 0 (lower left)
1, 0 (lower right)
0.6425, 1 (upper right)
0.3575, 1 (upper left)

The X (U?) coordinates seem to be described correctly, but the texture coordinate (0,0) must be the upper left corner, with the Y (V?) coordinate oriented downward. This might be some 2D version of the left-handed coordinate system, but that is not apparent or implied from the description in the 3DModel.pdf manual.

Anyway, now I think I have it figured out. Now on to making a usable instrument panel. Ain't this fun kids?

#### Thunder Chicken

Donator
Mocking up an appropriate instrument panel. The J-3 generally has five instruments, from left to right: tachometer, airspeed indicator, magnetic compass with spirit level and slip indicator, altimeter, and engine oil temperature / pressure indicator. No autopilots, no IFR. I just slapped these airspeed indicators in as placeholders as they have the bezels. Dimensions aren't exact at all but it's a pretty good caricature of a panel. There generally is a lot of open real estate at the top of the panel. I have images of the instrument faces for most of these, and I was planning to put animated needles on them to make them usable. The compass and slip indicators are going to be interesting to animate as they are not circles flat on the panel. Not all panels are wood, but I used that for visual interest and to remind the user that this isn't a hypersonic DeltaGlider.

After I get the instrument panel mostly happy I'll add some texture details on the exterior - the iconic black lightning stripes down the side, the Cub logo on the tail, etc..

#### Thunder Chicken

Donator
Looking at exterior textures - I got the lightning bolt texture UV coordinates and wrapping sorted out...but the background is transparent:

This is the PNG of the texture used to make the texture file with the transparent background:

I thought the background material color would show through? Do I need to apply that color to the texture?

#### Thunder Chicken

Donator
With the transparent background replaced with the Cub Yellow:

Looks like I need to texture the remaining surfaces with the basic Cub Yellow color so it all matches, but that's not the end of the world.

#### Thunder Chicken

Donator
Made a texture of pure Cub Yellow, 2 pixels by 2 pixels, and applied that to all the other surfaces:

Looks good. Only issue seems to be with the normal on the left side of the engine cowl - that should look like the left side of the fuselage but doesn't.

EDIT: I inadvertently left the material defined for the engine cowl. When I set MATERIAL 0 it cleans up:

#### Attachments

• cub_yellow.png
136 bytes · Views: 3
Last edited:

#### Thunder Chicken

Donator
End of today's work. Got the lightning stripes on both sides, and got a single texture to wrap around the vertical stabilizer with the Piper Cub logo on each side. UV coordinates and how the textures interact with the face normals makes sense to me now. I can work out the UV coordinates for the vertices that don't distort the textures on a spreadsheet.

#### Thunder Chicken

Donator
And now she's registered:

Heading back into the cockpit to complete the instrument textures and animate the instruments so I can get rid of the annotations. After that, I'll be polishing up the engine model a bit. Probably won't be building a really detailed model of a reciprocating engine, but something that determines the thrust of the propeller based on the shaft power delivered to it should be easy to implement and reasonably realistic.

#### Thunder Chicken

Donator
Instrument animations for tachometer, airspeed indicator, altimeter, and vertical speed indicators now work and give correct indications.

I also put in a rudimentary rudder autopilot to coordinate flight for those who don't have rudder pedals. I'd like to see if I can animate a toggle switch and a light to indicate that this feature is on. Animating the compass is also on my to-do list, but I'll have to make a mesh for the rotating indicator.

Getting close to something I am willing to release, just want to clean up some final details.

#### misha.physics

##### Well-known member
I liked this (yellow) plane very much. I'll wait for releasing to try it.

#### Thunder Chicken

Donator
So now I am trying to put a visual element on the instrument panel to indicate whether the autocoordinate feature is on or off. I've added a switch and some labels to the panel texture:

In the same texture file I have a second texture where this switch is in the ON position. The above texture covers UV coordinates from (0,0) to (1,0.5). The texture that has the switch on covers from (0,0.5) to (1,1). So to switch the texture, I need to edit the mesh group to add or subtract 0.5 from the V coordinates.

EDIT: I've updated my knowledge a bit so I am updating what I have for code.

I have the following in the SetClassCaps callback so it runs only once. It should make mesh group specs for the switch being ON and for the mesh being OFF:

Code:
hmesh = oapi.load_mesh_global('J-3/J-3')

oapi.get_meshgroup(hmesh, 23, grs) --get mesh group spec for instrument panel with switch OFF

--create new mesh group spec for instrument panel based on original, but edit V coordinates to set switch ON

ges = grs

for i = 1, ges.Vtx do
ges[i].tv = grs[i].tv + 0.5
end

Then I should be able to just edit the mesh group and re-assign the correct mesh group spec when the toggle key is pressed:

Code:
    if oapi.keydown(kstate, OAPI_KEY.A) then     -- toggle auto-coordinate rudder feature

if coordinate_hold == false then

coordinate_hold = true

oapi.edit_meshgroup (hmesh, 23, ges)

elseif coordinate_hold == true then

coordinate_hold = false

oapi.edit_meshgroup (hmesh, 23, grs)

end
return true
end

Orbiter starts when I run this script, but it stops loading at some point and I get errors due to the code that didn't execute. I have no syntax errors showing in VS Code and I'm not seeing the issue with the above code.

EDIT2: It seems it can't even get past the first get_meshgroup command. I tried assigning the return to message so I could print it out using the annotation, but the script just fails to load anything after this it seems. Orbiter does run, and I am sitting in the vantage point of the cockpit, but there is no vessel.

Code:
hmesh = oapi.load_mesh_global('J-3/J-3')

message = oapi.get_meshgroup(hmesh, 23, grs)

I've tried the {23} syntax for the mesh group number, doesn't make any difference. I can't see any errors that would give me any guidance on the problem.

Last edited:

#### Thunder Chicken

Donator
I am getting this error in Orbiter.log:

Code:
Config/Vessels/J3Script/J3Script.lua:103: bad argument #1 to 'get_meshgroup' (DEVMESHHANDLE expected, got userdata)

Apparently I need to provide a device-dependent mesh handle? I'm not sure what the difference is between DEVMESHHANDLE and just MESHHANDLE.

I also updated to the latest release of OpenOrbiter (May 14), no change in behavior, so this isn't a feature missing problem.

EDIT: Apparently this:

Code:
hmesh = oapi.load_mesh_global('J-3/J-3')

needed to be this:

Code:
hmesh = oapi.load_meshglobal('J-3/J-3')

Still can't get access to the meshgroup. I've tried all of the following with no luck (using Orbiter_ng.exe, Build 240514, in-line Orbiter.exe doesn't run for some reason after I updated?):

Code:
panel_switch_off = oapi.get_meshgroup(hmesh, 23) --bad argument #1 to 'get_meshgroup' (DEVMESHHANDLE expected, got userdata)
panel_switch_off = vi:get_meshgroup(hmesh, 23) --attempt to call method 'get_meshgroup' (a nil value)
panel_switch_off = oapi.get_mesh(hmesh, 23) --attempt to call field 'get_mesh' (a nil value)
panel_switch_off = vi:get_mesh(hmesh, 23) --attempt to call method 'get_mesh' (a nil value)
panel_switch_off = oapi.get_devmesh(hmesh, 23) --attempt to call field 'get_devmesh' (a nil value)
panel_switch_off = vi:get_devmesh(hmesh, 23) --get_devmesh: argument 2: invalid type (expected handle) userdata given

I'm not sure what I am doing wrong and going through the Interpreter code on GitHub and the Lua-fied DG isn't clarifying anything.

Last edited:

#### Thunder Chicken

Donator
Well, until I can figure out the cockpit meshes, I've gone back to vessel physics.

I've added three features. One was to dynamically adjust the wheel contact points to account for the shift in wheel contact point due to pitch.

The second is a flying tail, basically mimicking the jet of air coming off the propeller and being deflected by the rudder and/or elevators which is a real effect that provides some pitch and yaw authority at high engine output and low airspeed. This was done using set_thrusterdir, setting the direction based on the rudder and elevator deflections, and applying the thruster location at the tail of the vessel.

The third feature involved reinventing the wheel. "Wheels" in Orbiter generally are poorly modeled by simply assigning dynamic friction coefficients, usually high in the x-axis to minimize side slippage, and low in the direction of motion. I discussed these problems in detail here: https://www.orbiter-forum.com/threa...ake-issues-and-suggestions.40646/#post-599150.

So something I tried was to use add_force in the vessel x-direction, and set the force such that it forces the groundspeed vector in the x-direction relative to the vessel back to zero at each time step. The magnitude of the force is calculated as:

[imath]F \approx m \Delta V/\Delta t[/imath]

where m is the total mass instantaneous of the vessel (accounting for fuel consumption), [imath]\Delta V[/imath] is the deviation of the x-direction of velocity from zero, and [imath]\Delta t[/imath] is the simulation time step size simdt. This is calculated when the vessel is on the ground, and the force acts in the x-direction on a line between the contact points of the front main gear. Without some way to actually prescribe displacement this is about all that can be done. Once in a great while it calculated a large force and yeeted the J-3 into space, but with some under-relaxation that calmed down. I spent about half an hour taxiing my tail-dragger all around my airport:

It's pretty controllable right now, and I haven't even implemented differential braking yet.

#### LordCroussette

##### Quebec City's Resident Base Builder
Donator
Honestly it's pretty insane you were able to make meshes in notepad, of all things. Your plane is giving me huge early 80s Flight Sim vibes. Then again, I seriously doubt there were any kind of 3D modelling software in existence back then, so the models were most likely made the exact same way!

#### Thunder Chicken

Donator
Honestly it's pretty insane you were able to make meshes in notepad, of all things. Your plane is giving me huge early 80s Flight Sim vibes. Then again, I seriously doubt there were any kind of 3D modelling software in existence back then, so the models were most likely made the exact same way!
Yeah, it's the old school way of doing it for sure, but I learned a lot about meshes and textures doing things this way, which is helpful. For simple boxy aircraft this really isn't too hard to do. Calculating the normals and the UV coordinates for textures isn't too bad either if you understand vectors and mapping. Round things and curves are what drive the vertex count and complexity up. But even things like wheels can be meshed using a spreadsheet and a little math without too much agony. I am thinking of making some big bouncy tires for this plane eventually. I could certainly go into the mesh and maybe add some more triangles to make the wingtips and elevator and rudder more rounded, but a simple semi-realistic avatar for an aircraft is all I am really looking for. Smart use of textures can hide a lot of crude mesh if it is done right, and if it looks and acts like an aircraft then it must be one.

#### Thunder Chicken

Donator
Does anyone know if logical keys are defined in Lua? I am trying to get differential brakes sorted.

In SetClassCaps I have the maximum braking force set:

Code:
vi:set_maxwheelbrakeforce((empty_mass + main_fuel_tank_max) * 9.81 * 2)

If you do the math this should be enough braking force to plant the nose of the plane into the runway, so it should be obvious if it is applied. There seems to be some default braking action occurring, but it isn't applying the indicated brake force.

As far as applying the left or right brake, I need to set the brake level using vi:set_wheelbrakeforce, but I want to do that using the default brake keys (comma (,) and period (.)). I tried the following but it doesn't seem to do anything to the braking behavior. For some reason the braking is reversed (when I hit the left brake, the vessel turns right, and vice versa). The API Reference and the comments in the code base indicate "0 = average of both main gear levels, 1 = left, 2 = right", but that isn't what I am seeing.

Code:
    if oapi.keydown(kstate, OAPI_KEY.COMMA) then     -- set left brake

vi:set_wheelbrakelevel (1.0, 1, false)

return true

end

if oapi.keydown(kstate, OAPI_KEY.PERIOD) then     -- set right brake

vi:set_wheelbrakelevel (1.0, 2, false)

return true

end

In the API Reference there are "logical" keys that seem to be specific to their action, and I tried to Lua-fy them, but no luck:

Code:
    if oapi.keydown(kstate, oapi.LKEY_WheelbrakeLeft) then     -- set left brake

vi:set_wheelbrakelevel (1.0, 1, false)

return true

end

if oapi.keydown(kstate, oapi.LKEY_WheelbrakeRight) then     -- set right brake

vi:set_wheelbrakelevel (1.0, 2, false)

return true

end

I searched the Lua interpreter code on GitHub but did not find a Lua analog to these keys, and the API Reference description of logical keys really isn't helpful:

API Reference Page 236 said:
16.68.1 Detailed Description

<crickets chirping>

Anybody know what the deal is with setting brakes properly? I figured this would be easy but nothing seems to work correctly with regard to the brakes.

Last edited:

#### Thunder Chicken

Donator
If I comment everything out related to brakes in my code (all of the code in my previous post), the default braking is still applied, and that braking seems to be crossed. What is with that?

#### Thunder Chicken

Donator
I was able to fix the crossed brakes. That was due to the taildragger format of this aircraft. The second and third arguments of vi:set_touchdownpoints(tail_wheel_contact, right_wheel_contact, left_wheel_contact) must be the right and left wheel respectively, and apparently brakes are automatically applied to them.

I am able to get an additional force applied under the default brake keys, but it seems that the default force is always there? I tried disabling the default brakes by setting vi:set_maxwheelbrakeforce(0), but that doesn't seem to do a thing. It really seems that there is some braking baked deep into Orbiter and all of these brake methods aren't connected to it. I can't even seem to find how the default brake force is calculated.

Last edited:

#### Thunder Chicken

Donator
OK, I rolled my own wheels and brakes, and they work splendidly. I can lock up the brakes, do static engine run ups, pivot around one wheel when steering, set parking brakes, etc.. Here's what I have done:

Wheels

I basically apply a counter force to kill any momentum in the vessel x-direction at every time step as I described here: https://www.orbiter-forum.com/threa...ntures-and-questions.41799/page-3#post-620009

The following function is called in clbk_poststep:

Code:
function set_feature.Set_Rolling_Wheels()

--Following is to mimic realistic rolling tires without side scrub

if vi:get_groundcontact() then

local groundspeed = vi:get_groundspeedvector(REFFRAME.LOCAL)
local side_slip = groundspeed.x
local mass = empty_mass + vi:get_propellantmass(main_fuel_tank)

local simdt =  oapi.get_simstep()

side_force_new = mass * side_slip /simdt

if side_force_old == nil then

side_force_old = side_force_new

end

--Need to under-relax the force calculation so vessel doesn't get yeeted to Alpha Quadrant

side_force = side_force_old + 0.5*(side_force_new - side_force_old)

side_force_old = side_force

message = tostring(side_force)

else

side_force_old = 0

end

end

Brakes

It seems that Orbiter has some anemic default brakes in the background and I don't know if I have shut them down completely, but I have a separate braking implementation using add_force.

I have calls to apply either the left or right brake using the default brake keys in clbk_consumedirectkey:

Code:
function clbk_consumedirectkey(kstate)

if oapi.keydown(kstate, OAPI_KEY.COMMA) then     -- set left brake

set_feature.Set_Left_Brake_Force()

end

if oapi.keydown(kstate, OAPI_KEY.PERIOD) then     -- set right brake

set_feature.Set_Right_Brake_Force()

end

return false

end

The set_feature.Set_XXXX_Brake_Force() function simply calculates a braking force and applies it to the appropriate wheel contact (only left side function is shown, right is similar):

Code:
function set_feature.Set_Left_Brake_Force()

brake_force = set_feature.Apply_Brake_Force()

return true

end

The braking force is calculated using the following function. It calculates a braking force based on groundspeed in the z-direction, but limits the total braking acceleration to 1 G (9.81 m/s2). This will also apply braking force in the correct direction, for cases you are rolling backwards.

If groundspeed in the z-direction is zero, then the brake force applied exactly counters the z-component of thrust parallel to the ground.

Code:
function set_feature.Apply_Brake_Force()

if vi:get_groundcontact() then

local groundspeed = vi:get_groundspeedvector(REFFRAME.LOCAL)
local forward_speed = groundspeed.z
local mass = empty_mass + vi:get_propellantmass(main_fuel_tank)
local simdt =  oapi.get_simstep()

if forward_speed == 0 then --set brake force to counter thrust parallel to ground

F = vi:get_thrustvector(th_main)
pitch = vi:get_pitch()

brake_force = 0.5*F.z*math.cos(pitch)

else

if math.abs(forward_speed/simdt) > 9.81 then

--determine if velocity is positive or negative

if forward_speed > 0 then

brake_force = 0.5*mass*9.81

else

brake_force = -0.5*mass*9.81

end

else

brake_force = 0.5*mass*forward_speed/simdt

end

end

end

return brake_force

end

It occurs to me that this braking likely will fail if the vessel is not on completely flat terrain as the wheel and braking forces don't currently account for vessel x- and z- components of gravity, so if you decide to go off-roading in a J-3, good luck.

Replies
2
Views
131
Replies
0
Views
76
Replies
1
Views
291
Replies
17
Views
1K
Replies
3
Views
480