1541 lines
50 KiB
Plaintext
Executable File
1541 lines
50 KiB
Plaintext
Executable File
-- terrain_helperfunctions.ms
|
|
-- created by Gunnar Droege
|
|
-- written by Luke Openshaw
|
|
-- ripped out of the collision set tool for general use
|
|
-- 10/06/2010
|
|
--
|
|
-- Neal D Corbett 05/2014 - Rewritten as generalised methods for processing terrain-shaders
|
|
|
|
fileIn (RsConfigGetWildWestDir() + "script/3dsMax/_common_functions/FN_RSTA_string.ms")
|
|
|
|
global gRsTerrainHelpers
|
|
|
|
-- Returns value used to match two colours together; the higher the value, the better the match:
|
|
-- (ClrA/B should be normalised point3 values)
|
|
fn RsGetAlphaVal ClrA ClrB =
|
|
(
|
|
-- Alpha corresponds to r/g/b matches:
|
|
local Alph = (ClrA * ClrB) + (1.0 - ClrA) * (1.0 - ClrB)
|
|
|
|
-- Return combined alpha-value:
|
|
return (Alph.X * Alph.Y * Alph.Z)
|
|
)
|
|
|
|
-- Descriptor for a terrain-material shader-type:
|
|
-- (Default settings are for GTA5-style 'Terrain_Cb_*' blended shaders)
|
|
struct RsTerrainShaderType
|
|
(
|
|
-- Flags for quickly checking shader-type:
|
|
isUberTerrain = False,
|
|
isStandard2lyr = False,
|
|
|
|
-- The behaviour of a terrain-material depends on which type of terrain-shader it's using:
|
|
Type = #Terrain_Cb,
|
|
|
|
-- String-pattern for distiguishing shader-types via their names:
|
|
pattern = "Terrain_Cb_*",
|
|
|
|
-- Colours used to denote which diffuse-texture should be used where:
|
|
LookupColours = #(Black, Blue, Green, (color 0 255 255), Red, (color 255 0 255), Yellow, White),
|
|
LookupColourNames = #("black", "blue", "green", "turquoise", "red", "purple", "yellow", "white"),
|
|
NormLookupColours,
|
|
|
|
-- Colour-indices that are safe to blend to from the colour with a given index:
|
|
LookupSafeCombos = #(#{1,2,3,5},#{1,2,4,6},#{1,3,4,7},#{2,3,4,8},#{1,5,6,7},#{2,5,6,8},#{3,5,7,8},#{4,6,7,8}),
|
|
|
|
-- UV-channels used to place Texmaps/Lookup-maps/Tint-maps:
|
|
-- (default Texmap/LookupUVchans can be changed per-material for some shaders)
|
|
texmapUVchan = 1, lookupUVchan = 2, tintUVchan = 3,
|
|
|
|
-- Will be set to list of channels that LookupUVchan can be set to:
|
|
LookupUVchans = #{},
|
|
|
|
-- Lookup/Tint vertex-colour channels:
|
|
LookupClrChan = 9, TintClrChan = 0,
|
|
|
|
-- Channels used to blend between Texture/Vertex-Colour versions of Lookup/Tint:
|
|
-- Lookup is masked on vertex-alpha channel by default
|
|
maskChan = -2, lookupMaskSubChan, tintMaskSubChan,
|
|
|
|
-- Channels for masking displacement/curvature effects:
|
|
effectsMaskChan, displaceMaskSubChan, curvatureMaskSubChan,
|
|
|
|
-- Default mask-values, to be used on slider-controls:
|
|
DefLookupMaskVal = 1.0, DefTintMaskVal = 0.0, DefDisplaceMaskVal = 0.0,
|
|
|
|
-- Text description of mask-channels:
|
|
lookupMaskChanText = "Vertex Alpha", tintMaskChanText = "", displaceMaskChanText = "",
|
|
|
|
-- Text description of mask min/max values:
|
|
LookupMaskRangeText = DataPair Min:"Texture" Max:"Vertex",
|
|
TintMaskRangeText = DataPair Min:"Texture" Max:"Vertex",
|
|
DisplaceMaskRangeText = DataPair Min:"Show" Max:"Hide",
|
|
|
|
-- Function for deconstructing a given normalised colour-value to texmap-blend weights: (this alias is set by 'On Create')
|
|
GetWeightsFromClr,
|
|
|
|
-- Returns value used to match two colours together; the higher the value, the better the match:
|
|
-- (ClrA/B should be normalised point3 values)
|
|
fn GetAlphaVal clrA clrB =
|
|
(
|
|
-- Alpha corresponds to r/g/b matches:
|
|
local alph = (clrA * clrB) + (1.0 - clrA) * (1.0 - clrB)
|
|
|
|
-- Return combined alpha-value:
|
|
return (alph.x * alph.y * alph.z)
|
|
),
|
|
|
|
-- (converted from 'BlendTerrainColor' in 'terrain_cb_common.fxh')
|
|
fn GetWeightsFromClr_Terrain_Cb clrVal texCount: =
|
|
(
|
|
if (texCount == unsupplied) do
|
|
(
|
|
texCount = normLookupColours.count
|
|
)
|
|
|
|
for idx = 1 to texCount collect
|
|
(
|
|
GetAlphaVal clrVal normLookupColours[idx]
|
|
)
|
|
),
|
|
-- (converted from 'cpvBlendWeights' in 'terrain_uber_common.fxh')
|
|
fn GetWeightsFromClr_Terrain_Uber clrVal =
|
|
(
|
|
local rVal = clrVal[1]
|
|
local gVal = clrVal[2]
|
|
local bVal = clrVal[3]
|
|
local blackVal = (1.0 - (rVal + gVal + bVal))
|
|
if (blackVal < 0) do (blackVal = 0)
|
|
|
|
-- Return blend-weights calculated for clrVal.
|
|
-- Uber lookup-colours are red/green/blue/black:
|
|
return #(rVal, gVal, bVal, blackVal)
|
|
),
|
|
-- Greyscale values:
|
|
fn GetWeightsFromClr_Standard2Lyr clrVal =
|
|
(
|
|
local whiteVal = clrVal[1]
|
|
local blackVal = (1.0 - whiteVal)
|
|
return #(blackVal, whiteVal)
|
|
),
|
|
|
|
-- Function for constructing colour-value from given blend-weights:
|
|
-- (inverse function to 'GetWeightsFromClr')
|
|
GetClrFromWeights,
|
|
|
|
fn GetClrFromWeights_Terrain_Cb blends =
|
|
(
|
|
-- Construct blend-colour colour up by adding weighted lookup-colours:
|
|
local OutClr = [0,0,0]
|
|
for Idx = 1 to 4 do
|
|
(
|
|
OutClr += (Blends[Idx] * NormLookupColours[Idx])
|
|
)
|
|
|
|
return OutClr
|
|
),
|
|
fn GetClrFromWeights_Terrain_Uber blends =
|
|
(
|
|
-- Just return first three blend-weights as rgb values:
|
|
return [Blends[1],Blends[2],Blends[3]]
|
|
),
|
|
fn GetClrFromWeights_Standard2Lyr blends =
|
|
(
|
|
-- Just return white-value:
|
|
local whiteVal = blends[2]
|
|
return [whiteVal, whiteVal, whiteVal]
|
|
),
|
|
|
|
on Create do
|
|
(
|
|
-- Convert lookup-colours to normalised point3 values:
|
|
this.normLookupColours = for clr in this.lookupColours collect ((clr as Point3) / 255.0)
|
|
|
|
-- Choose appropriate blend-weight function for shader:
|
|
this.GetWeightsFromClr = case type of
|
|
(
|
|
#Terrain_Uber:(this.GetWeightsFromClr_Terrain_Uber)
|
|
#Standard_2lyr:(this.GetWeightsFromClr_Standard2Lyr)
|
|
Default:(this.GetWeightsFromClr_Terrain_Cb)
|
|
)
|
|
this.GetClrFromWeights = case type of
|
|
(
|
|
#Terrain_Uber:(this.GetClrFromWeights_Terrain_Uber)
|
|
#Standard_2lyr:(this.GetClrFromWeights_Standard2Lyr)
|
|
Default:(this.GetClrFromWeights_Terrain_Cb)
|
|
)
|
|
|
|
-- Get list of Lookup UV channels this shadertype might use:
|
|
this.lookupUVchans = case type of
|
|
(
|
|
#Terrain_Uber:(#{1..3})
|
|
#Standard_2lyr:(#{1..2})
|
|
Default:(#{this.lookupUVchan})
|
|
)
|
|
)
|
|
)
|
|
|
|
-- TTN shaders are like default Terrain_Cb, but with Tint-texmap,
|
|
-- and mask-channel to blend between using Lookup/Tint texmaps or vertex-colours:
|
|
global RsTerrainShaderType_TTN =
|
|
(
|
|
RsTerrainShaderType Type:#Terrain_Cb_Ttn \
|
|
Pattern:"Terrain_Cb_*_Ttn"
|
|
)
|
|
|
|
-- Default: GTA5-style terrain-shaders:
|
|
-- (only 'cm' versions have the 'Lookup texture' slot)
|
|
global RsTerrainShaderType_CB =
|
|
(
|
|
RsTerrainShaderType()
|
|
)
|
|
|
|
-- RDR3/Americas-style terrain-shaders:
|
|
global RsTerrainShaderType_Uber =
|
|
(
|
|
RsTerrainShaderType Type:#Terrain_Uber \
|
|
Pattern:"Terrain_Uber_*" \
|
|
LookupColours:#(Red, Green, Blue, Black) \
|
|
LookupColourNames:#("red", "green", "blue", "black") \
|
|
LookupSafeCombos:#() \ -- (Uber doesn't appear to show blending-artifacts)
|
|
LookupUVchan:1 TintUVchan:1 \ -- Lookup/Tint textures use same UVs as diffuse-maps (diffuse-tiling is changed via shader values)
|
|
MaskChan:5 \ -- Lookup/Tint are both masked by this vert-channel
|
|
TintMaskSubChan:1 TintMaskChanText:"Vert-Channel 5: Red" \ -- Tint is masked on red sub-channel
|
|
LookupMaskSubChan:2 LookupMaskChanText:"Vert-Channel 5: Green" \ -- Lookup is masked on green sub-channel
|
|
DisplaceMaskSubChan:3 DisplaceMaskChanText:"Vert-Channel 5: Blue" -- Displacement is masked on blue sub-channel
|
|
)
|
|
|
|
-- List of differences between terrain-shader types:
|
|
global RsTerrainShaderTypes =
|
|
#(
|
|
RsTerrainShaderType_TTN,
|
|
RsTerrainShaderType_CB,
|
|
RsTerrainShaderType_Uber
|
|
)
|
|
|
|
-- Descriptor for a terrain-material.
|
|
-- Should be initialised with 'TerrainMat' argument.
|
|
struct RsTerrainMatInfo
|
|
(
|
|
Private
|
|
-- Faces this material and its diffuse-texmaps are used on:
|
|
-- 'MatFaces' is set up by 'GetObjTerrainFaceData', 'DiffuseFaces' by 'FindDominantFaceTextures'
|
|
MatFaces = #{}, DiffuseFaces = #(),
|
|
|
|
Public
|
|
TerrainMat,
|
|
|
|
-- 'PresetName' is the value in material's 'Preset' slot, which will be the shader-name or a Material Preset.
|
|
-- If this is a Material Preset, the name of the actual shader will be saved to ShaderName.
|
|
-- 'ShaderName' is matched against RsTerrainShaderTypes:
|
|
PresetName, ShaderName, TypeDef,
|
|
|
|
DiffuseTexPaths = #(), BumpTexPaths = #(), TexCount = 0,
|
|
LookupTexPath, TintTexPath,
|
|
|
|
-- The material-slots containing this material's diffuse/bump texmaps:
|
|
DiffSlotNums = #(), BumpSlotNums = #(),
|
|
|
|
-- Object and MatId this material is used on:
|
|
Obj, MatId = undefined,
|
|
|
|
-- Material's shader-values, updated by 'UpdateVals':
|
|
VarNames = #(), VarTypes = #(), VarNums = #(), VarVals = #(),
|
|
|
|
-- Track tool-selection of texmap-indices:
|
|
Selected = #{},
|
|
|
|
|
|
------------------------------------------------------------------------------------------------------
|
|
-- Accessor-functions for private arrays:
|
|
------------------------------------------------------------------------------------------------------
|
|
fn GetMatFaces = (return MatFaces),
|
|
fn GetDiffuseFaces = (return DiffuseFaces),
|
|
|
|
------------------------------------------------------------------------------------------------------
|
|
-- GetLookupUVchan:
|
|
-- Returns the Lookup UV channel for this descriptor's material.
|
|
-- This can currently only be changed on a per-material basis for Terrain_Uber shaders.
|
|
------------------------------------------------------------------------------------------------------
|
|
fn GetLookupUVchan =
|
|
(
|
|
-- Default value
|
|
local uvChan = typeDef.lookupUVchan
|
|
|
|
-- Get channel-value from material, where supported:
|
|
local matChan = case of
|
|
(
|
|
(typeDef.isUberTerrain):(RstGetVariableByName this.terrainMat "Lookup UV Set")
|
|
(typeDef.isStandard2lyr):(RstGetVariableByName this.terrainMat "Control UV Set")
|
|
Default:Undefined
|
|
)
|
|
|
|
-- Use channel from material:
|
|
if (matChan != undefined) do
|
|
(
|
|
uvChan = (Integer matChan)
|
|
)
|
|
|
|
return uvChan
|
|
),
|
|
|
|
------------------------------------------------------------------------------------------------------
|
|
-- GetTexmapUVchan:
|
|
-- Returns the UV channel used to map a materials texmap.
|
|
-- This can currently only be changed on a per-texmap basis for Terrain_Uber shaders.
|
|
------------------------------------------------------------------------------------------------------
|
|
fn GetTexmapUVchan texNum =
|
|
(
|
|
-- Default mapping-channel:
|
|
local uvChan = typeDef.texmapUVchan
|
|
|
|
case of
|
|
(
|
|
(typeDef.isUberTerrain):
|
|
(
|
|
-- PXM shaders will use the default UV channel (1)
|
|
if (not isPxmShader) do
|
|
(
|
|
-- Use material's 'UV Set' variable, if supported by shader:
|
|
local texChans = (RstGetVariableByName this.terrainMat "UV Set")
|
|
|
|
if (texChans != undefined) do
|
|
(
|
|
uvChan = (Integer texChans[texNum])
|
|
)
|
|
)
|
|
)
|
|
(typeDef.isStandard2lyr):
|
|
(
|
|
local varName = case texNum of
|
|
(
|
|
1:"Lyr1 UV Set"
|
|
2:"Lyr2 UV Set"
|
|
)
|
|
|
|
uvChan = Integer (RstGetVariableByName this.terrainMat varName)
|
|
)
|
|
)
|
|
|
|
return uvChan
|
|
),
|
|
|
|
------------------------------------------------------------------------------------------------------
|
|
-- UpdateVals:
|
|
-- Get/update Material info:
|
|
------------------------------------------------------------------------------------------------------
|
|
fn UpdateVals =
|
|
(
|
|
-- Get material's current preset/shader-name:
|
|
local NewPresetName = (RstGetShaderName TerrainMat)
|
|
|
|
-- Clear path-arrays/values:
|
|
(
|
|
for ThisArray in #(DiffuseTexPaths, BumpTexPaths) do
|
|
(
|
|
ThisArray.Count = 0
|
|
)
|
|
|
|
LookupTexPath = undefined
|
|
TintTexPath = undefined
|
|
)
|
|
|
|
-- Update details if shadername has changed since:
|
|
if (NewPresetName != PresetName) do
|
|
(
|
|
-- Clear various values/arrays:
|
|
(
|
|
for ThisArray in #(VarNames, VarTypes, VarNums, VarVals, Selected, DiffuseFaces) do
|
|
(
|
|
ThisArray.Count = 0
|
|
)
|
|
|
|
TexCount = 0
|
|
)
|
|
|
|
-- Update shader-name stuff:
|
|
(
|
|
PresetName = NewPresetName
|
|
|
|
-- Get shadername for preset,if used:
|
|
-- ('RstGetMaterialPresetShaderName' isn't currently defined in GTA5 tools)
|
|
if (RstGetMaterialPresetShaderName != undefined) do
|
|
(
|
|
ShaderName = (RstGetMaterialPresetShaderName TerrainMat)
|
|
)
|
|
|
|
-- If material isn't using a Preset, use 'PresetName' as 'ShaderName':
|
|
if (ShaderName == undefined) do
|
|
(
|
|
ShaderName = PresetName
|
|
)
|
|
|
|
-- Strip extension from ShaderName:
|
|
ShaderName = (GetFilenameFile ShaderName)
|
|
|
|
-- Find matching terrain-type for material:
|
|
TypeDef = undefined
|
|
for ThisType in RsTerrainShaderTypes while (TypeDef == undefined) do
|
|
(
|
|
if (MatchPattern ShaderName Pattern:ThisType.Pattern) do
|
|
(
|
|
TypeDef = ThisType
|
|
)
|
|
)
|
|
)
|
|
|
|
-- Update attribute-names/types if this is a valid terrain-shader:
|
|
if (TypeDef != undefined) do
|
|
(
|
|
local NumVars = (RstGetVariableCount TerrainMat)
|
|
|
|
-- Find shader's texmap-slots, used for texmap-swapping:
|
|
local TexSlotNum = 0
|
|
for VarNum = 1 to NumVars do
|
|
(
|
|
local VarType = (RstGetVariableType TerrainMat VarNum)
|
|
|
|
-- We're only interested in Texmap and Vector4 attributes:
|
|
if (varType == "texmap") or (varType == "vector4") or (varType == "float") do
|
|
(
|
|
local VarName = (RstGetVariableName TerrainMat VarNum)
|
|
append VarNames VarName
|
|
append VarNums VarNum
|
|
append VarTypes VarType
|
|
|
|
if (VarType == "texmap") do
|
|
(
|
|
TexSlotNum += 1
|
|
|
|
case of
|
|
(
|
|
-- Get diffuse-maps:
|
|
((matchpattern VarName pattern:"Diffuse*") or (matchpattern VarName pattern:"Color Texture*")):
|
|
(
|
|
append DiffuseFaces #{}
|
|
append DiffSlotNums TexSlotNum
|
|
)
|
|
-- Remember bump slot-numbers:
|
|
(matchPattern VarName pattern:"Bump *"):
|
|
(
|
|
append BumpSlotNums TexSlotNum
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
-- Get diffusemap-slot count:
|
|
TexCount = DiffSlotNums.Count
|
|
)
|
|
)
|
|
|
|
-- Stop processing if this isn't a valid terrain-shader:
|
|
if (TypeDef == undefined) do return OK
|
|
|
|
-- Collect updated attribute-values:
|
|
for VarIdx = 1 to VarNums.Count do
|
|
(
|
|
local VarNum = VarNums[VarIdx]
|
|
local ThisVal = (RstGetVariable TerrainMat VarNum)
|
|
VarVals[VarIdx] = ThisVal
|
|
|
|
-- Collect updated texturemap-name data:
|
|
if (VarTypes[VarIdx] == "texmap") do
|
|
(
|
|
local VarName = VarNames[VarIdx]
|
|
|
|
case of
|
|
(
|
|
-- Get diffuse-maps:
|
|
((matchpattern VarName pattern:"Diffuse*") or (matchpattern VarName pattern:"Color Texture*")):
|
|
(
|
|
append DiffuseTexPaths ThisVal
|
|
)
|
|
-- Remember bump slot-numbers:
|
|
(matchPattern VarName pattern:"Bump *"):
|
|
(
|
|
append BumpTexPaths ThisVal
|
|
)
|
|
-- Get lookup-texture path, if used:
|
|
(matchpattern VarName pattern:"Lookup texture"):
|
|
(
|
|
LookupTexPath = ThisVal
|
|
)
|
|
-- Get tint-texture path, if used:
|
|
(matchpattern VarName pattern:"Tint"):
|
|
(
|
|
TintTexPath = ThisVal
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
return OK
|
|
),
|
|
|
|
------------------------------------------------------------------------------------------------------
|
|
-- GetTexPath:
|
|
-- Returns diffuse/bump-map path with specific index:
|
|
------------------------------------------------------------------------------------------------------
|
|
fn GetTexPath TexNum Bump:False =
|
|
(
|
|
local TexArray = if Bump then BumpTexPaths else DiffuseTexPaths
|
|
local ThisPath = TexArray[TexNum]
|
|
|
|
if (ThisPath == undefined) do
|
|
(
|
|
ThisPath = ""
|
|
)
|
|
|
|
return ThisPath
|
|
),
|
|
|
|
-------------------------------------------------------------------------------------------
|
|
-- GetFaceVertTexWeights:
|
|
-- Returns lists of blend-weights for each vert on each face in 'MatFaces',
|
|
-- Lookup-bitmaps and masks are ignored if 'IgnoreBitmaps' is true,
|
|
-------------------------------------------------------------------------------------------
|
|
fn GetFaceVertTexWeights faceLookupClrVerts: ignoreBitmaps:False quiet:False escToAbort:False perFace:False =
|
|
(
|
|
-- Abort if material has no faces, or is an invalid object:
|
|
if (this.matFaces.numberSet == 0) or not ((IsValidNode obj) or (IsKindOf obj TriMesh)) do return OK
|
|
|
|
local faceLookupClrVertsUnsupplied = (faceLookupClrVerts == Unsupplied)
|
|
if faceLookupClrVertsUnsupplied do
|
|
(
|
|
faceLookupClrVerts = #()
|
|
)
|
|
|
|
local promptMsg = ("Collecting texmap blend-weights... [matId: " + (matId as String) + "]")
|
|
if escToAbort do
|
|
(
|
|
Append promptMsg " [Esc to Cancel]"
|
|
)
|
|
PushPrompt (RsProgressString PromptMsg 0)
|
|
|
|
-- Set up aliases to appropriate functions for this object:
|
|
local ObjOp = RsMeshPolyOp Obj
|
|
local isMeshObj = (ObjOp == MeshOp)
|
|
local ObjGetFace = RsGetFaceFunc obj
|
|
local ObjGetMapFace = RsGetMapFaceFunc obj
|
|
local ObjGetMapVert = ObjOp.GetMapVert
|
|
|
|
-- Get appropriate vert-channels from the TypeDef:
|
|
local LookupClrChan = TypeDef.LookupClrChan
|
|
local LookupMapChan = This.GetLookupUVchan()
|
|
local MaskChan = TypeDef.MaskChan
|
|
local LookupMaskSubChan = TypeDef.LookupMaskSubChan
|
|
|
|
-- Are we processing Standard_2lyr shader?
|
|
local isStandard2lyr = typeDef.isStandard2lyr
|
|
|
|
-- Do we need to get pixels and mask for Lookup texture?
|
|
-- (non-vertcolour channels are ignored for 'IgnoreBitmaps' mode)
|
|
local HasLookupTex = (not IgnoreBitmaps) and (LookupTexPath != undefined)
|
|
local UsesMask = (not isStandard2lyr) and (not IgnoreBitmaps) and (HasLookupTex) and (TypeDef.MaskChan != undefined)
|
|
local UsesLookupClr = (LookupClrChan != undefined)
|
|
local UsesLookupMap = (not IgnoreBitmaps) and (LookupMapChan != undefined)
|
|
|
|
-- Get typedef's function for finding a colour's texmap-blend weights:
|
|
local GetWeightsFromClr = TypeDef.GetWeightsFromClr
|
|
|
|
-- Are required channels actually available?
|
|
local LookupClrChanAvailable = (UsesLookupClr and (ObjOp.GetMapSupport Obj LookupClrChan))
|
|
local LookupMapChanAvailable = (UsesLookupMap and (ObjOp.GetMapSupport Obj LookupMapChan))
|
|
local MaskChanAvailable = (UsesMask and (ObjOp.GetMapSupport Obj MaskChan))
|
|
|
|
-- Get blend-value stuff for Standard_2lyr shader:
|
|
if isStandard2lyr do
|
|
(
|
|
local blendSoftness = (RstGetVariableByName this.terrainMat "Blend Softness")
|
|
local controlFilter = (RstGetVariableByName this.terrainMat "Control Filter")
|
|
local invertControl = ((RstGetVariableByName this.terrainMat "Invert Control") == 1.0)
|
|
|
|
-- This needs to be a Point3 to be used with Point3 colours
|
|
if (IsKindOf controlFilter Point4) do
|
|
controlFilter = controlFilter as Point3
|
|
|
|
-- Find blend-exponent:
|
|
local blendExp = (blendSoftness ^ 2)
|
|
if (blendExp < 0.00001) do
|
|
(
|
|
blendExp = 0.00001
|
|
)
|
|
blendExp = (1.0 / blendExp)
|
|
)
|
|
|
|
-- Standard_2lyr needs to load alpha from Control Texture:
|
|
local texClrClass = if isStandard2lyr then Point4 else Point3
|
|
|
|
-- Warn about missing channels, where required:
|
|
if (not Quiet) do
|
|
(
|
|
for Item in
|
|
#(
|
|
DataPair Text:("Lookup-colour (channel " + (LookupClrChan as string) + ")") Error:(UsesLookupClr and not LookupClrChanAvailable),
|
|
DataPair Text:("Lookup-texture UVs (channel " + (LookupMapChan as string) + ")") Error:(UsesLookupMap and not LookupMapChanAvailable),
|
|
DataPair Text:("Mask-colour (channel " + (MaskChan as string) + ")") Error:(UsesMask and not MaskChanAvailable)
|
|
) where Item.Error do
|
|
(
|
|
format "WARNING: % is missing on object" Item.Text
|
|
if (isValidNode Obj) do (format " '%'" Obj.Name) -- Don't print name for TriMesh Obj-values
|
|
format " - this is required by material ID % (we'll assume channel is black)\n" matId
|
|
)
|
|
)
|
|
|
|
-- Lookup-bitmap data will be loaded to here, if required:
|
|
local LookupBmp, BmpMaxX, BmpMaxY
|
|
local BmpLoadFailed = False
|
|
|
|
-- Don't bother attempting to load lookup-bitmap if the relevant uv-channel is missing:
|
|
-- (lookup-bitmap will be assumed to be all black)
|
|
if (not LookupMapChanAvailable) do
|
|
(
|
|
BmpLoadFailed = True
|
|
)
|
|
|
|
-- Use arbitrary rgb subchannel for lookup-mask if subchannel was unspecified...
|
|
local UseLookupMaskSubChan = LookupMaskSubChan
|
|
if (UseLookupMaskSubChan == undefined) do
|
|
(
|
|
UseLookupMaskSubChan = 1
|
|
)
|
|
|
|
-- UV/colour vert-data for this material, so we'll only need to probe each vert once:
|
|
local LookupMapVertClrs = #()
|
|
if LookupMapChanAvailable do (LookupMapVertClrs.Count = (ObjOp.GetNumMapVerts Obj LookupMapChan))
|
|
|
|
-- Collect UV face-vert lists - these are used by other functions too:
|
|
if LookupClrChanAvailable do
|
|
(
|
|
join FaceLookupClrVerts (for FaceNum in MatFaces collect (ObjGetMapFace Obj LookupClrChan FaceNum))
|
|
)
|
|
|
|
-- Get number of faces used by material:
|
|
local MatFacesCount = (this.matFaces.numberSet)
|
|
|
|
-- We expect this operation will require about this much memory:
|
|
local blendsCount = this.texCount
|
|
local FaceVertsEstimate = if isMeshObj then 3 else 4
|
|
local MemReq = (Integer64 MatFacesCount * FaceVertsEstimate * BlendsCount * 32)
|
|
local MemDiff = (HeapFree - MemReq)
|
|
--format "MemReq:% HeapFree:% MemDiff: %\n" MemReq HeapFree MemDiff
|
|
|
|
-- Increase heapsize if we think it's going to be too small:
|
|
-- (otherwise Max may auto-increase it thousands of times during loop, very slow)
|
|
if (MemDiff < 0) do
|
|
(
|
|
HeapSize += (abs MemDiff)
|
|
)
|
|
|
|
-- Predefine array:
|
|
local FaceBlendWeights = for n = 1 to MatFacesCount collect #()
|
|
|
|
-- Update prompt: (overriding any heap-resize messages)
|
|
ReplacePrompt (RsProgressString PromptMsg 0)
|
|
|
|
-- We'll update the status-prompt every so often:
|
|
local PromptUpdateInterval = (MatFacesCount / 21)
|
|
|
|
-- Find dominant lookup-weightings for each material's faces' verts:
|
|
local Success = True
|
|
local FaceIdx = 0
|
|
for FaceNum in This.MatFaces while (Success = not (EscToAbort and Keyboard.EscPressed)) do
|
|
(
|
|
FaceIdx += 1
|
|
|
|
-- Update statusprompt progressbar every so often:
|
|
if ((Mod FaceIdx PromptUpdateInterval) == 0) do
|
|
(
|
|
ReplacePrompt (RsProgressString PromptMsg (1.0 * FaceIdx / MatFacesCount))
|
|
)
|
|
|
|
-- This empty blendweights-array will be initially filled with lookup-colours...
|
|
local FaceLookupClrs = FaceBlendWeights[FaceIdx]
|
|
|
|
local FaceLookupMask = #()
|
|
|
|
-- Set to False if all vert-colours are to be taken from lookup-bitmap:
|
|
local HasLookupVertClrs = True
|
|
|
|
-- Get mask-values for face's verts, if shader uses Lookup-mask:
|
|
if HasLookupTex do
|
|
(
|
|
-- Default this to false for Terrain (for shaders that have Lookup-texture but no masking feature)
|
|
-- Standard_2lyr uses both texture and vertex values, and has no masking
|
|
hasLookupVertClrs = if isStandard2lyr then True else False
|
|
|
|
local hasLookupTexClrs = True
|
|
|
|
-- Get data from mask-channel, if used by this terrain-shader type:
|
|
if usesMask do
|
|
(
|
|
-- Only process mask-channel if it's actually active - otherwise, we'll default to assuming it is white
|
|
if MaskChanAvailable then
|
|
(
|
|
-- Get verts used on mask-channel face:
|
|
local FaceMaskVerts = (ObjGetMapFace Obj MaskChan FaceNum)
|
|
|
|
-- Get mask-values for each of mask-face's verts:
|
|
faceLookupMask = for mapVertNum in faceMaskVerts collect
|
|
(
|
|
-- Get colour-value from mask-channel:
|
|
local maskClr = (ObjGetMapVert obj maskChan mapVertNum)
|
|
|
|
-- Get mask-values for Lookup-texture:
|
|
maskClr[useLookupMaskSubChan]
|
|
)
|
|
faceMaskVerts.count = 0
|
|
|
|
-- Do we need to load colours from texture or vertcolours or both for this face?
|
|
HasLookupVertClrs = False
|
|
HasLookupTexClrs = False
|
|
for VertMask in FaceLookupMask do
|
|
(
|
|
if (VertMask > 0) do
|
|
(
|
|
-- This face takes some colours from Lookup vertex-channel:
|
|
HasLookupVertClrs = True
|
|
)
|
|
if (VertMask < 1) do
|
|
(
|
|
-- This face takes some colours from Lookup texture:
|
|
HasLookupTexClrs = True
|
|
)
|
|
)
|
|
)
|
|
else
|
|
(
|
|
-- If mask-channel is missing, we'll assume it's default white:
|
|
HasLookupTexClrs = False
|
|
HasLookupVertClrs = True
|
|
)
|
|
)
|
|
|
|
-- Collect unmasked colours, from lookup-bitmap:
|
|
if hasLookupTexClrs do
|
|
(
|
|
-- Load bitmap, it will be needed:
|
|
-- (this will happen once per material)
|
|
if (not BmpLoadFailed) and (LookupBmp == undefined) do
|
|
(
|
|
LookupBmp = OpenBitmap LookupTexPath
|
|
|
|
-- Check to see if bitmap failed to load:
|
|
if (LookupBmp == undefined) then
|
|
(
|
|
BmpLoadFailed = True
|
|
)
|
|
else
|
|
(
|
|
BmpMaxX = (LookupBmp.Width - 1)
|
|
BmpMaxY = (LookupBmp.Height - 1)
|
|
)
|
|
)
|
|
|
|
-- Collect lookup-colours for this face's verts:
|
|
-- Use black as default if lookup-bitmap failed to load:
|
|
local LookupClrs = if (BmpLoadFailed) then
|
|
(
|
|
-- This works even if the mapping-channel is inactive:
|
|
for ThisVert in (ObjGetFace Obj FaceNum) collect [0,0,0]
|
|
)
|
|
else
|
|
(
|
|
-- Get verts used on lookup-texture's map-face:
|
|
local FaceMapVerts = ObjGetMapFace Obj LookupMapChan FaceNum
|
|
|
|
-- Collect bitmap-colours for each unmasked vert:
|
|
for VertIdx = 1 to FaceMapVerts.Count collect
|
|
(
|
|
-- Get mask-value for vert (zero (black) if shader doesn't do masking, or 1 (white) if mask-channel is missing)
|
|
local VertMask = case of
|
|
(
|
|
(UsesMask and MaskChanAvailable):FaceLookupMask[VertIdx]
|
|
UsesMask:1
|
|
Default:0
|
|
)
|
|
|
|
-- Collect undefined if bitmap is masked for this vert...
|
|
local LookupClr = undefined
|
|
|
|
-- Get bitmap-colour from texturemap if vert is unmasked:
|
|
if (vertMask < 1) do
|
|
(
|
|
-- Get vertex-number from mapping-face vert-list:
|
|
local MapVertNum = FaceMapVerts[VertIdx]
|
|
|
|
-- Get cached colour for vert:
|
|
LookupClr = LookupMapVertClrs[MapVertNum]
|
|
|
|
-- If not cached, get UVs from, mesh, and colour from bitmap:
|
|
if (LookupClr == undefined) do
|
|
(
|
|
local UvPos = ObjGetMapVert Obj LookupMapChan MapVertNum
|
|
|
|
-- Convert UV-coords to texture-coords:
|
|
local BmpPos = [integer (abs (mod UvPos.x 1.0) * BmpMaxX), integer ((1 - (abs (mod UvPos.y 1.0))) * BmpMaxY)]
|
|
|
|
-- Get pixel-colour:
|
|
LookupClr = (GetPixels LookupBmp BmpPos 1)[1]
|
|
|
|
-- Convert to nomalised Point3/Point4:
|
|
LookupClr = (LookupClr as texClrClass) / 255
|
|
|
|
-- Cache pixel-colour:
|
|
LookupMapVertClrs[MapVertNum] = LookupClr
|
|
)
|
|
)
|
|
|
|
-- Collect colour from bitmap:
|
|
LookupClr
|
|
)
|
|
)
|
|
|
|
-- Join colours-array to this empty array (this maintains array-references)
|
|
join FaceLookupClrs LookupClrs
|
|
)
|
|
|
|
-- (Finished loading face's colours from Lookup-texture)
|
|
)
|
|
|
|
-- Get colours from Lookup vert-colours, if face's mask allows this:
|
|
if hasLookupVertClrs do
|
|
(
|
|
local ThisFaceLookupClrVerts
|
|
|
|
if LookupClrChanAvailable do
|
|
(
|
|
-- Get vert-indices for face's vert-colour Lookup-colours:
|
|
ThisFaceLookupClrVerts = FaceLookupClrVerts[FaceIdx]
|
|
|
|
-- Expand face's colours-array if it is still empty:
|
|
FaceLookupClrs.Count = ThisFaceLookupClrVerts.Count
|
|
)
|
|
|
|
-- Get vert-colours for verts that aren't masked to use Lookup texture:
|
|
for VertIdx = 1 to FaceLookupClrs.Count do
|
|
(
|
|
local VertMask = if (UsesMask and MaskChanAvailable) then FaceLookupMask[VertIdx] else 1
|
|
|
|
-- Only bother getting vert-colour if this vert was masked at all - otherwise we just use colour from Lookup texture.
|
|
if (VertMask > 0) do
|
|
(
|
|
local VertClr
|
|
|
|
if (LookupClrChanAvailable) then
|
|
(
|
|
local MapVertNum = ThisFaceLookupClrVerts[VertIdx]
|
|
|
|
-- Get normalised Lookup vertex-colour: (on 0-1 scale)
|
|
vertClr = ObjGetMapVert obj lookupClrChan mapVertNum
|
|
|
|
case of
|
|
(
|
|
-- Apply Control Texture values to Standard_2lyr vertex-blend:
|
|
isStandard2lyr:
|
|
(
|
|
-- Get 'Control Amount' from texmap:
|
|
local bmpVertClr = faceLookupClrs[vertIdx]
|
|
local controlAmount = (Dot controlFilter bmpVertClr)
|
|
if invertControl do
|
|
(
|
|
controlAmount = (1.0 / controlAmount)
|
|
)
|
|
|
|
-- Find blend-value:
|
|
local vertClrVal = vertClr[1]
|
|
local blendBase = (vertClrVal * controlAmount) + vertClrVal
|
|
if (blendBase > 1.0) do
|
|
(
|
|
blendBase = 1.0
|
|
)
|
|
local blendVal = (blendBase ^ blendExp)
|
|
vertClr = [blendVal,blendVal,blendVal]
|
|
)
|
|
|
|
-- If vert isn't fully-masked, mix it with the colour taken from the lookup-texture:
|
|
(vertMask < 1):
|
|
(
|
|
local bmpVertClr = faceLookupClrs[vertIdx]
|
|
|
|
-- Combine texmap/vertex lookup-colours using mask-value as alpha:
|
|
vertClr = (vertMask * vertClr) + ((1 - vertMask) * bmpVertClr)
|
|
)
|
|
)
|
|
)
|
|
else
|
|
(
|
|
-- Default to black if lookup-channel is missing:
|
|
VertClr = [0,0,0]
|
|
)
|
|
|
|
FaceLookupClrs[VertIdx] = VertClr
|
|
)
|
|
)
|
|
)
|
|
|
|
-- This array is no longer needed:
|
|
FaceLookupMask.Count = 0
|
|
|
|
-- Now we have the colours for each of face's verts (blended between Lookup-vertcolours and texturemap as required)
|
|
-- Extract texmap blend-weights for each vert:
|
|
for VertIdx = 1 to FaceLookupClrs.Count do
|
|
(
|
|
local vertClr = FaceLookupClrs[VertIdx]
|
|
|
|
-- Replace array-colour with its corresponding blend-weights:
|
|
FaceLookupClrs[VertIdx] = (GetWeightsFromClr vertClr texCount:texCount)
|
|
)
|
|
)
|
|
|
|
-- All done! Now close lookup-bitmap, if one has been opened:
|
|
if (LookupBmp != undefined) do
|
|
(
|
|
Close LookupBmp
|
|
)
|
|
|
|
-- Clear finished arrays:
|
|
lookupMapVertClrs.count = 0
|
|
|
|
-- Only clear this array if it was created inside this function:
|
|
if (faceLookupClrVertsUnsupplied) do
|
|
(
|
|
for item in faceLookupClrVerts do
|
|
(
|
|
item.count = 0
|
|
)
|
|
faceLookupClrVerts.count = 0
|
|
)
|
|
|
|
PopPrompt()
|
|
|
|
if Success then (return FaceBlendWeights) else (return False)
|
|
),
|
|
|
|
-------------------------------------------------------------------------------------------
|
|
-- GetFaceTexWeights:
|
|
-- Returns lists of blend-weights for each face in 'MatFaces',
|
|
-- Lookup-bitmaps and masks are ignored if 'IgnoreBitmaps' is true,
|
|
-------------------------------------------------------------------------------------------
|
|
fn GetFaceTexWeights ignoreBitmaps:False quiet:False escToAbort:False =
|
|
(
|
|
-- Get per-vertex weights, or face-weights from lookup-render:
|
|
local faceVertWeights = this.GetFaceVertTexWeights perFace:True ignoreBitmaps:ignoreBitmaps quiet:quiet escToAbort:escToAbort
|
|
|
|
-- Return non-array error-values:
|
|
if (not isKindOf faceVertWeights Array) do return faceVertWeights
|
|
if (faceVertWeights.count == 0) do return #()
|
|
|
|
-- If this is a list of weights per vertex per face, convert to list of weights per face:
|
|
if (IsKindOf faceVertWeights[1][1] Array) do
|
|
(
|
|
local blendsCount = this.texCount
|
|
|
|
for faceIdx = 1 to faceVertWeights.count do
|
|
(
|
|
local vertWeights = faceVertWeights[faceIdx]
|
|
|
|
if (vertWeights.count != 0) do
|
|
(
|
|
local avgWeights = vertWeights[1]
|
|
for vertIdx = 2 to vertWeights.count do
|
|
(
|
|
local thisVertWeights = vertWeights[vertIdx]
|
|
for texIdx = 1 to blendsCount do
|
|
(
|
|
avgWeights[texIdx] += thisVertWeights[texIdx]
|
|
)
|
|
)
|
|
avgWeights = for thisWeight in avgWeights collect (thisWeight / blendsCount)
|
|
faceVertWeights[faceIdx] = avgWeights
|
|
)
|
|
)
|
|
)
|
|
|
|
return faceVertWeights
|
|
),
|
|
|
|
-------------------------------------------------------------------------------------------
|
|
-- HasMatchingTexIdx:
|
|
-- Returns idx of highest BlendWeight
|
|
-------------------------------------------------------------------------------------------
|
|
fn GetDominantTexIdx BlendWeights =
|
|
(
|
|
FindItem BlendWeights (Amax BlendWeights)
|
|
),
|
|
|
|
-------------------------------------------------------------------------------------------
|
|
-- HasMatchingTexIdx:
|
|
-- Used to decide whether a subobject uses a given texmap or not.
|
|
-- [Default] True if TexIdx has non-zero weighting
|
|
-- [Dominant:True] True if TexIdx has the highest weighting
|
|
-------------------------------------------------------------------------------------------
|
|
fn HasMatchingTexIdx BlendWeights TexIdx Dominant:False =
|
|
(
|
|
if Dominant then
|
|
(
|
|
return ((GetDominantTexIdx BlendWeights) == TexIdx)
|
|
)
|
|
else
|
|
(
|
|
return (BlendWeights[TexIdx] != 0.0)
|
|
)
|
|
),
|
|
|
|
-- Returns list of verts displaying texmap with index 'TexIdx';
|
|
fn GetVertsUsingTexmap TexIdx IgnoreBitmaps:False Dominant:False =
|
|
(
|
|
-- Return verts for all matfaces if no specific TexIdx was requested:
|
|
if (TexIdx == 0) then
|
|
(
|
|
-- Get list of verts used by matfaces:
|
|
local MatVerts = #()
|
|
local ObjGetFace = (RsGetFaceFunc Obj)
|
|
|
|
for FaceNum in MatFaces do
|
|
(
|
|
join MatVerts (ObjGetFace Obj FaceNum)
|
|
)
|
|
|
|
-- Return verts for faces:
|
|
return (MatVerts as BitArray)
|
|
)
|
|
else
|
|
(
|
|
local FaceGeomVerts = #()
|
|
|
|
-- Get vertex texture-blends data:
|
|
local FaceBlendWeights = GetFaceTexWeights FaceGeomVerts:FaceGeomVerts IgnoreBitmaps:IgnoreBitmaps
|
|
|
|
-- Abort if that failed:
|
|
if (not isKindOf FaceBlendWeights Array) do (return OK)
|
|
|
|
local TexVerts = #{}
|
|
|
|
-- Examine blend-weights on verts used by each face:
|
|
for FaceIdx = 1 to FaceBlendWeights.Count do
|
|
(
|
|
local FaceBlends = FaceBlendWeights[FaceIdx]
|
|
local FaceVertNums = FaceGeomVerts[FaceIdx]
|
|
|
|
for VertIdx = 1 to FaceBlends.Count do
|
|
(
|
|
-- Set bit for geometry-verts if weighting matches TexIdx:
|
|
if (HasMatchingTexIdx FaceBlends[VertIdx] TexIdx Dominant:Dominant) do
|
|
(
|
|
local VertNum = FaceVertNums[VertIdx]
|
|
TexVerts[VertNum] = True
|
|
)
|
|
)
|
|
)
|
|
|
|
return TexVerts
|
|
)
|
|
),
|
|
|
|
-- Returns list of faces displaying texmap with index 'TexIdx';
|
|
fn GetFacesUsingTexmap TexIdx IgnoreBitmaps:False Dominant:False =
|
|
(
|
|
-- Return all matfaces if no specific TexIdx was requested:
|
|
if (TexIdx == 0) do return MatFaces
|
|
|
|
-- Get vertex texture-blends data:
|
|
local FaceBlendWeights = GetFaceTexWeights IgnoreBitmaps:IgnoreBitmaps
|
|
|
|
-- Abort if that failed:
|
|
if (not isKindOf FaceBlendWeights Array) do (return OK)
|
|
|
|
local TexFaces = #{}
|
|
(
|
|
-- Initialise face-array sizes:
|
|
local MatFacesArray = (MatFaces as Array)
|
|
local MaxFaceNum = MatFacesArray[MatFacesArray.Count]
|
|
MatFacesArray.Count = 0
|
|
TexFaces.Count = MaxFaceNum
|
|
)
|
|
|
|
local FaceNums = (MatFaces as Array)
|
|
|
|
-- Examine blend-weights on verts used by each face:
|
|
for FaceIdx = 1 to FaceBlendWeights.Count do
|
|
(
|
|
local FaceHasTex = False
|
|
local FaceBlends = FaceBlendWeights[FaceIdx]
|
|
|
|
if Dominant then
|
|
(
|
|
-- Add faces' vert-weights together:
|
|
local CombinedBlends = for TexIdx = 1 to TexCount collect
|
|
(
|
|
local Val = 0
|
|
for VertBlends in FaceBlends do
|
|
(
|
|
Val += VertBlends[TexIdx]
|
|
)
|
|
Val
|
|
)
|
|
|
|
-- Find dominant texmap for combined blend-weights, and see if it matches TexIdx:
|
|
FaceHasTex = ((GetDominantTexIdx CombinedBlends) == TexIdx)
|
|
)
|
|
else
|
|
(
|
|
-- Does this face include a vert with non-zero blend for this texmap?
|
|
for VertBlends in FaceBlends while (not FaceHasTex) do
|
|
(
|
|
FaceHasTex = (HasMatchingTexIdx VertBlends TexIdx Dominant:False)
|
|
)
|
|
)
|
|
|
|
if FaceHasTex do
|
|
(
|
|
local FaceNum = FaceNums[FaceIdx]
|
|
TexFaces[FaceNum] = True
|
|
)
|
|
)
|
|
|
|
return TexFaces
|
|
),
|
|
|
|
------------------------------------------------------------------------------------------
|
|
-- SwapTexmapLookups:
|
|
-- Swaps lookup-colours for two texmap-ids.
|
|
------------------------------------------------------------------------------------------
|
|
fn SwapTexmapLookups TexIdxA TexIdxB =
|
|
(
|
|
-- Abort if both idxs are the same for some reason...
|
|
if (TexIdxA == TexIdxB) do return OK
|
|
|
|
-- Get vertex texture-blends data for material's faces:
|
|
local FaceLookupClrVerts = #()
|
|
local FaceBlendWeights = GetFaceTexWeights FaceLookupClrVerts:FaceLookupClrVerts IgnoreBitmaps:True
|
|
|
|
-- Abort if that failed:
|
|
if (not isKindOf FaceBlendWeights Array) or (FaceLookupClrVerts.Count != FaceBlendWeights.Count) do (return FaceBlendWeights)
|
|
|
|
-- Set up aliases to appropriate functions for this object:
|
|
local ObjOp = RsMeshPolyOp Obj
|
|
local ObjSetMapVert = ObjOp.SetMapVert
|
|
local ObjGetMapFace = RsGetMapFaceFunc Obj
|
|
|
|
-- We'll be processing the Lookup vertexcolour-channel:
|
|
local Chan = TypeDef.LookupClrChan
|
|
|
|
-- Get typedef's function for converting texmap-blend weights to colour-values:
|
|
local GetClrFromWeights = TypeDef.GetClrFromWeights
|
|
|
|
-- Get map-verts per lookup-channel face, to compare against 'FaceBlendWeights':
|
|
local LookupClrChan = TypeDef.LookupClrChan
|
|
|
|
-- Bitarray to keep track of which mapverts have already been processed:
|
|
local VertsDone = #{}
|
|
VertsDone.Count = (ObjOp.GetNumMapVerts Obj LookupClrChan)
|
|
|
|
-- Process all mapverts that have weights available:
|
|
for FaceIdx = 1 to FaceBlendWeights.Count do
|
|
(
|
|
-- Get blends and UV-indices for this face:
|
|
local FaceBlends = FaceBlendWeights[FaceIdx]
|
|
local FaceVerts = FaceLookupClrVerts[FaceIdx]
|
|
|
|
-- Process face's verts in turn:
|
|
for VertIdx = 1 to FaceVerts.Count do
|
|
(
|
|
local VertNum = FaceVerts[VertIdx]
|
|
|
|
-- Don't process mapverts more than once each:
|
|
if (not VertsDone[VertNum]) do
|
|
(
|
|
VertsDone[VertNum] = True
|
|
local VertWeights = FaceBlends[VertIdx]
|
|
|
|
-- Create new weights-array, where TexIdxA/B are swapped:
|
|
local NewWeights = for TexIdx = 1 to TexCount collect
|
|
(
|
|
case TexIdx of
|
|
(
|
|
TexIdxA:VertWeights[TexIdxB]
|
|
TexIdxB:VertWeights[TexIdxA]
|
|
Default:VertWeights[TexIdx]
|
|
)
|
|
)
|
|
|
|
-- Generate colour from swapped texmap-weights, and edit that lookup-vert:
|
|
local NewClr = GetClrFromWeights NewWeights
|
|
ObjSetMapVert Obj Chan VertNum NewClr
|
|
)
|
|
)
|
|
)
|
|
|
|
-- Swap the values for this struct's path/face arrays:
|
|
for ThisArray in #(DiffuseTexPaths, DiffuseFaces) do
|
|
(
|
|
local OldValA = ThisArray[TexIdxA]
|
|
ThisArray[TexIdxA] = ThisArray[TexIdxB]
|
|
ThisArray[TexIdxB] = OldValA
|
|
)
|
|
|
|
-- Update onscreen colours:
|
|
case ObjOp of
|
|
(
|
|
polyOp:(polyOp.collapseDeadStructs Obj)
|
|
meshOp:(update Obj)
|
|
)
|
|
CompleteRedraw()
|
|
|
|
return OK
|
|
),
|
|
|
|
------------------------------------------------------------------------------------------
|
|
-- SwapTexmapMatSlots, SwapTexmapSlotsByTexIdx:
|
|
-- Swaps material's texmap-slots for two texmap-ids.
|
|
------------------------------------------------------------------------------------------
|
|
fn SwapTexmapMatSlots SlotNumA SlotNumB =
|
|
(
|
|
--format "Swapping texmap slots: %/%\n" SlotNumA SlotNumB
|
|
local SlotATexmap = GetSubTexmap TerrainMat SlotNumA
|
|
local SlotBTexmap = GetSubTexmap TerrainMat SlotNumB
|
|
SetSubTexmap TerrainMat SlotNumA SlotBTexmap
|
|
SetSubTexmap TerrainMat SlotNumB SlotATexmap
|
|
SlotBTexmap.Filename = SlotBTexmap.Filename
|
|
),
|
|
fn SwapTexmapSlotsByTexIdx TexIdxA TexIdxB =
|
|
(
|
|
-- Swap diffuse and bump slots:
|
|
for ThisArray in #(DiffSlotNums, BumpSlotNums) where (ThisArray.Count != 0) do
|
|
(
|
|
SwapTexmapMatSlots ThisArray[TexIdxA] ThisArray[TexIdxB]
|
|
)
|
|
|
|
-- Update shader after swaps are completed:
|
|
RstRefreshMtl TerrainMat
|
|
),
|
|
|
|
------------------------------------------------------------------------------------------
|
|
-- SwapTexmaps:
|
|
-- Swaps lookup-colours or material texmap-slots for two texmap-ids.
|
|
------------------------------------------------------------------------------------------
|
|
fn SwapTexmaps TexIdxA TexIdxB SwapMatSlots:True SwapLookups:True =
|
|
(
|
|
if (TexIdxA == TexIdxB) do return False
|
|
|
|
if SwapMatSlots do
|
|
(
|
|
SwapTexmapSlotsByTexIdx TexIdxA TexIdxB
|
|
)
|
|
if SwapLookups do
|
|
(
|
|
SwapTexmapLookups TexIdxA TexIdxB
|
|
)
|
|
),
|
|
|
|
-------------------------------------------------------------------------------------------
|
|
-- FindDominantFaceTextures:
|
|
-- Works out which diffusemap is most prominent on each of material's faces
|
|
-- (This function takes lookup-texture/mask into account)
|
|
-------------------------------------------------------------------------------------------
|
|
fn FindDominantFaceTextures Quiet:False IgnoreBitmaps:False EscToAbort:False =
|
|
(
|
|
-- Get texture-blends data for this material-definition:
|
|
local FaceBlendWeights = (GetFaceTexWeights IgnoreBitmaps:IgnoreBitmaps EscToAbort:EscToAbort)
|
|
|
|
-- Abort if that failed:
|
|
if (not isKindOf FaceBlendWeights Array) do (return False)
|
|
|
|
-- Get number of faces used by material:
|
|
local MatFacesCount = 0
|
|
local MaxMatFaceNum = 0
|
|
for n in This.MatFaces do
|
|
(
|
|
MatFacesCount += 1
|
|
MaxMatFaceNum = n
|
|
)
|
|
|
|
-- Initialise face-array sizes:
|
|
for TexFaces in DiffuseFaces do
|
|
(
|
|
TexFaces.Count = MaxMatFaceNum
|
|
)
|
|
|
|
local FacesCount = (FaceBlendWeights.Count)
|
|
|
|
local PromptMsg = ("Finding dominant texmap index... [matId: " + (matId as String) + "]")
|
|
if EscToAbort do
|
|
(
|
|
append PromptMsg " [Esc to Cancel]"
|
|
)
|
|
PushPrompt (RsProgressString PromptMsg 0)
|
|
|
|
-- We'll update the status-prompt every so often:
|
|
local PromptUpdateInterval = (FacesCount / 21)
|
|
|
|
-- Examine blend-weights on verts used by each face:
|
|
local faceIdx = 0
|
|
local multiVertWeights = False
|
|
for FaceNum in This.MatFaces do
|
|
(
|
|
faceIdx += 1
|
|
|
|
-- Update statusprompt's progressbar every so often:
|
|
if ((Mod FaceIdx PromptUpdateInterval) == 0) do
|
|
(
|
|
ReplacePrompt (RsProgressString PromptMsg (1.0 * FaceIdx / FacesCount))
|
|
)
|
|
|
|
local faceBlends = faceBlendWeights[FaceIdx]
|
|
|
|
-- Is 'faceBlends' a single array of texmap-weights, or an array of per-vert weight-arrays?
|
|
if (faceIdx == 1) and (IsKindOf faceBlends[1] Array) do
|
|
(
|
|
multiVertWeights = True
|
|
)
|
|
|
|
local combinedBlends = if (not multiVertWeights) then faceBlends else
|
|
(
|
|
-- Add face's vert-weights together:
|
|
for texIdx = 1 to texCount collect
|
|
(
|
|
local val = 0
|
|
for vertBlends in faceBlends do
|
|
(
|
|
val += vertBlends[texIdx]
|
|
)
|
|
val
|
|
)
|
|
)
|
|
|
|
-- Find dominant texmap for combined blend-weights:
|
|
local texIdx = GetDominantTexIdx CombinedBlends
|
|
|
|
-- We can now assign this face to the matching diffuse-path's face-list:
|
|
DiffuseFaces[TexIdx][FaceNum] = True
|
|
)
|
|
|
|
PopPrompt()
|
|
|
|
return True
|
|
),
|
|
|
|
-------------------------------------------------------------------------------------------
|
|
-- GetNameString:
|
|
-- Generate name-string to show in tools
|
|
-------------------------------------------------------------------------------------------
|
|
fn GetNameString =
|
|
(
|
|
local MatText = StringStream ""
|
|
|
|
if (MatId != undefined) and (MatId != -1) do
|
|
(
|
|
format "%: " MatId To:MatText
|
|
)
|
|
format "% [" TerrainMat.Name To:MatText
|
|
|
|
-- Show shadername value if material is using a preset:
|
|
if ((GetFilenameFile PresetName) != ShaderName) do
|
|
(
|
|
format "% | " ShaderName To:MatText
|
|
)
|
|
format "%]" PresetName To:MatText
|
|
|
|
return (MatText as String)
|
|
),
|
|
|
|
-------------------------------------------------------------------------------------------
|
|
-- Create:
|
|
-- Initialises struct to match describe TerrainMat
|
|
-- Works out flavour of Terrain shader is in use (if any) and collects textures
|
|
-------------------------------------------------------------------------------------------
|
|
on Create do
|
|
(
|
|
-- Abort if 'TerrainMat' is undefined or invalid:
|
|
if (not isKindOf TerrainMat Rage_Shader) do return False
|
|
|
|
-- Get material shader-data:
|
|
UpdateVals()
|
|
|
|
return OK
|
|
)
|
|
)
|
|
|
|
struct RsTerrainHelpers
|
|
(
|
|
-------------------------------------------------------------------------------------------------
|
|
-- GetObjTerrainFaceData:
|
|
-- Returns list of 'RsTerrainMatInfo' descriptors of materials with
|
|
-- compatible terrain-shaders (these are defined by array 'RsTerrainShaderTypes')
|
|
-- If 'MatchMat' is supplied, function only returns descriptor for that submaterial.
|
|
-- (if submaterial was found on object, and uses compatible shader)
|
|
-------------------------------------------------------------------------------------------------
|
|
fn GetObjTerrainFaceData Obj Material: MatchMat: =
|
|
(
|
|
if (not isValidObj Obj) do return #()
|
|
|
|
-- Materials used on object, and their respective faces:
|
|
local ObjMats = #()
|
|
local FaceLists = #()
|
|
local MtlIdList = #()
|
|
|
|
-- Get material-info for object:
|
|
PushPrompt "Collecting material-info for object..."
|
|
RsGetMaterialsOnObjFaces Obj Material:Material Materials:ObjMats FaceLists:FaceLists MtlIdList:MtlIdList
|
|
PopPrompt()
|
|
|
|
-- Generate and collect terrain-material info-structs for each material found:
|
|
PushPrompt "Processing materials..."
|
|
local MatInfoList = for MatIdx = 1 to ObjMats.Count collect
|
|
(
|
|
local SubMat = ObjMats[MatIdx]
|
|
|
|
-- Skip this submaterial if if doesn't match the supplied 'MatchMat':
|
|
if (MatchMat != unsupplied) and (MatchMat != SubMat) then DontCollect else
|
|
(
|
|
local NewMatInfo = RsTerrainMatInfo TerrainMat:ObjMats[MatIdx] Obj:Obj MatId:MtlIdList[MatIdx]
|
|
|
|
-- Set up private array - set up list of faces used by material:
|
|
local MatFaces = NewMatInfo.GetMatFaces()
|
|
join MatFaces FaceLists[MatIdx]
|
|
|
|
-- Collect MatInfo struct:
|
|
NewMatInfo
|
|
)
|
|
)
|
|
PopPrompt()
|
|
|
|
-- Filter out materials without valid terrain-shaders:
|
|
MatInfoList = for Item in MatInfoList where (Item.TypeDef != undefined) collect Item
|
|
|
|
-- Sort materials by matid:
|
|
qsort MatInfoList (fn SortByMatId v1 v2 = (v1.MatId - v2.MatId))
|
|
|
|
return MatInfoList
|
|
),
|
|
|
|
-------------------------------------------------------------------------------------------------
|
|
-- GetDominantFaceTexInfo:
|
|
-- Collects compatible-shader descriptors for 'Obj',
|
|
-- and triggers collection of per-face dominant-texture data.
|
|
-------------------------------------------------------------------------------------------------
|
|
fn GetDominantFaceTexInfo Obj IgnoreBitmaps:False EscToAbort:False Material: MatchMat: =
|
|
(
|
|
local TimeStart = TimeStamp()
|
|
|
|
local MatInfoList = GetObjTerrainFaceData Obj Material:Material MatchMat:MatchMat
|
|
|
|
-- Collect dominant-textures face-data for listed material-structs:
|
|
PushPrompt "Finding dominant textures for terrain-faces..."
|
|
local Success = True
|
|
for Item in MatInfoList while Success and (Success = not (EscToAbort and Keyboard.EscPressed)) do
|
|
(
|
|
Success = Item.FindDominantFaceTextures IgnoreBitmaps:IgnoreBitmaps EscToAbort:EscToAbort
|
|
)
|
|
PopPrompt()
|
|
|
|
format "Dominant-texmap search took % seconds\n" ((TimeStamp() - TimeStart) / 1000.0)
|
|
|
|
return MatInfoList
|
|
),
|
|
|
|
------------------------------------------------------------------------------------------
|
|
-- BalanceTexWeights:
|
|
-- Balance texmap-colour weights - they should add up to 1.0
|
|
------------------------------------------------------------------------------------------
|
|
fn BalanceTexWeights texWeights =
|
|
(
|
|
local totalWeight = 0
|
|
for texWeight in texWeights do
|
|
(
|
|
totalWeight += texWeight
|
|
)
|
|
if (totalWeight != 1) do
|
|
(
|
|
local mult = (1.0 / totalWeight)
|
|
texWeights = for texWeight in texWeights collect (texWeight * mult)
|
|
)
|
|
|
|
return texWeights
|
|
)
|
|
)
|
|
gRsTerrainHelpers = RsTerrainHelpers()
|
|
|
|
-- Legacy function:
|
|
fn GetDominantTextureFromTerrainFace obj mat matID faceId =
|
|
(
|
|
-- Get submaterial:
|
|
local MatchMat = unsupplied
|
|
if (isKindOf Mat MultiMaterial) and (isKindOf MatId Integer) do
|
|
(
|
|
local MatIdx = FindItem Mat.MaterialIDList MatID
|
|
|
|
if (MatIdx != 0) do
|
|
(
|
|
MatchMat = Mat.MaterialList[MatIdx]
|
|
)
|
|
)
|
|
|
|
-- Collect dominant-texture terrain-material data from object:
|
|
local MatInfoList = gRsTerrainHelpers.GetDominantFaceTexInfo Obj Material:Mat MatchMat:MatchMat
|
|
|
|
-- Search collected data for material/texmap that uses 'FaceId':
|
|
local MatFound = False
|
|
local DiffusePath = undefined
|
|
for MatItem in MatInfoList while (not MatFound) do
|
|
(
|
|
if (MatItem.GetMatFaces())[FaceId] do
|
|
(
|
|
MatFound = True
|
|
|
|
local TexFound = False
|
|
|
|
for TexNum = 1 to MatItem.DiffuseTexPaths.Count while (not TexFound) do
|
|
(
|
|
if MatItem.DiffuseFaces[TexNum][FaceId] do
|
|
(
|
|
TexFound = True
|
|
DiffusePath = MatItem.DiffuseTexPaths[TexNum]
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
return DiffusePath
|
|
)
|
|
|
|
-- TEST --
|
|
IF FALSE DO
|
|
(
|
|
clearlistener()
|
|
print "IF YOU CAN SEE THIS, SOMEBODY LEFT THE TEST-CODE IN"
|
|
gc()
|
|
local TimeStart = TimeStamp()
|
|
local obj = $
|
|
|
|
--local stuff = (gRsTerrainHelpers.GetObjTerrainFaceData Obj EscToAbort:True)
|
|
local stuff = (gRsTerrainHelpers.GetDominantFaceTexInfo Obj EscToAbort:True) --IgnoreBitmaps:False)
|
|
--local stuff = (gRsTerrainHelpers.GetDominantFaceTexInfo (copy Obj.Mesh) Material:Obj.Material EscToAbort:True) --IgnoreBitmaps:False)
|
|
--local stuff = (gRsTerrainHelpers.GetObjTerrainFaceData (copy Obj.Mesh) Material:Obj.Material EscToAbort:True) --IgnoreBitmaps:False)
|
|
|
|
--local Verts = stuff[2].GetVertsUsingTexmap 0
|
|
|
|
format "Took % seconds\n" ((timeStamp() - timeStart) / 1000.0)
|
|
|
|
--stuff[1].GetFaceTexWeights()
|
|
|
|
print stuff
|
|
local MatThing = stuff[1]
|
|
print (MatThing.GetMatFaces()).numberset
|
|
for n = 1 to 4 do
|
|
(
|
|
print (MatThing.GetDiffuseFaces())[n].numberset
|
|
)
|
|
print (MatThing.GetFacesUsingTexmap 1)
|
|
print (MatThing.GetFacesUsingTexmap 2)
|
|
print (MatThing.GetFacesUsingTexmap 3)
|
|
print (MatThing.GetFacesUsingTexmap 4)
|
|
--MatThing.SwapTexmaps 1 2 SwapMatSlots:False SwapLookups:True
|
|
|
|
RsTerrainShaderTypes[3].GetClrFromWeights (RsTerrainShaderTypes[3].GetWeightsFromClr [0.1,0.5,0.1])
|
|
--ok
|
|
)
|