Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion addons/godotvmf/entities/func_detail.gd
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ func _entity_setup(entity: VMFEntity):
return;

$MeshInstance3D.set_mesh(mesh);
$MeshInstance3D/StaticBody3D/CollisionShape3D.shape = $MeshInstance3D.mesh.create_trimesh_shape();
$MeshInstance3D/StaticBody3D/CollisionShape3D.shape = mesh.create_trimesh_shape();
12 changes: 11 additions & 1 deletion addons/godotvmf/godotmdl/mdl_combiner.gd
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,8 @@ func generate_collision():
vertices.append_array([v1, v2, v3]);

collision.shape.set_faces(PackedVector3Array(vertices));
if skeleton and skeleton.find_bone("static_body") == -1:

if skeleton and skeleton.find_bone("static_prop") == -1:
var bone_attachment: BoneAttachment3D = BoneAttachment3D.new();
bone_attachment.name = "bone_attachment_" + str(surface_index) + "_" + str(solid_index);
bone_attachment.bone_idx = max(0, solid.bone_index - 1);
Expand Down Expand Up @@ -246,12 +247,21 @@ func assign_materials():


for tex in mdl.textures:
var found = false
for dir in mdl.textureDirs:
var path = VMFUtils.normalize_path(dir + "/" + tex.name);
if not VMTLoader.has_material(path.to_lower()): continue;
var material = VMTLoader.get_material(path.to_lower());
if not material: continue;
materials.append(material);
found = true
break
if not found:
var path = VMFUtils.normalize_path(tex.name);
if VMTLoader.has_material(path.to_lower()):
var material = VMTLoader.get_material(path.to_lower());
if material:
materials.append(material);

var surfaces = mesh_instance.mesh.get_surface_count();
var skin_id = 0;
Expand Down
10 changes: 7 additions & 3 deletions addons/godotvmf/godotmdl/reader.gd
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ static func read_by_structure(file: FileAccess, Clazz, read_from = -1):

return result;

static func read_char(file: FileAccess):
var byte = file.get_8();
return char(byte) if byte != 0 else "";

static func _read_data(file: FileAccess, type: Type):
match type:
Type.FLOAT: return file.get_float();
Expand All @@ -84,10 +88,10 @@ static func _read_data(file: FileAccess, type: Type):
Type.SHORT: return read_signed_short(file);
Type.LONG: return file.get_64() - 0x80000000;
Type.UNSIGNED_SHORT: return file.get_16();
Type.UNSIGNED_CHAR: return file.get_8();
Type.UNSIGNED_CHAR: return read_char(file);

Type.STRING: return char(file.get_8());
Type.CHAR: return char(file.get_8());
Type.STRING: return read_char(file);
Type.CHAR: return read_char(file);
Type.VECTOR2: return Vector2(file.get_float(), file.get_float());
Type.VECTOR4: return Vector4(file.get_float(), file.get_float(), file.get_float(), file.get_float());

Expand Down
20 changes: 15 additions & 5 deletions addons/godotvmf/godotvmf.gd
Original file line number Diff line number Diff line change
Expand Up @@ -77,25 +77,33 @@ func GetExistingVMFNodes() -> Array[VMFNode]:
nodes.assign(get_tree().get_nodes_in_group("vmfnode_group"));
return nodes.filter(func(node): return not node.get_meta("instance", false));

func _on_import_progress(phase: String, current: int, total: int):
var label = dock.get_node_or_null('ProgressBar/label');
if label:
label.text = "%s (%d/%d)" % [phase, current, total];

func ReimportVMF():
var nodes := GetExistingVMFNodes();

dock.get_node('ProgressBar').show();
await get_tree().create_timer(0.1).timeout

for node in nodes:
node.import_map();
node.import_progress.connect(_on_import_progress);
await node.import_map();
node.import_progress.disconnect(_on_import_progress);
dock.get_node('ProgressBar').hide();

func ReimportEntities():
var nodes := GetExistingVMFNodes();

dock.get_node('ProgressBar').show();
dock.get_node('ProgressBar').show();
await get_tree().create_timer(0.1).timeout

for node in nodes:
node.reimport_entities();
for node in nodes:
node.import_progress.connect(_on_import_progress);
await node.reimport_entities();
node.import_progress.disconnect(_on_import_progress);
dock.get_node('ProgressBar').hide();

func ReimportGeometry():
Expand All @@ -105,5 +113,7 @@ func ReimportGeometry():
await get_tree().create_timer(0.1).timeout

for node in nodes:
node.reimport_geometry();
node.import_progress.connect(_on_import_progress);
await node.reimport_geometry();
node.import_progress.disconnect(_on_import_progress);
dock.get_node('ProgressBar').hide();
33 changes: 33 additions & 0 deletions addons/godotvmf/godotvmt/formats/vtf_dxt.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
class_name VTFDXT extends VTFFrameReader

static func read(vtf: VTFLoader, frame: int, srgb_conversion_method: VTFLoader.SRGBConversionMethod) -> ImageTexture:
var data = PackedByteArray();
var bytes_read := 0;
var is_dxt_1 := vtf.hires_image_format == vtf.ImageFormat.DXT1;
var multiplier = 8 if is_dxt_1 else 16;
var format := vtf.format_map[str(vtf.hires_image_format)] as Image.Format;
var use_mipmaps := not (vtf.flags & vtf.Flags.TEXTUREFLAGS_NOMIP);

frame = vtf.frames - 1 - frame;

for i in range(vtf.mipmap_count):
var mipWidth = max(1, vtf.width >> i);
var mipHeight = max(1, vtf.height >> i);

var mip_size = max(1, mipWidth / 4) * max(1, mipHeight / 4) * multiplier;

vtf.file.seek(vtf.file.get_length() - bytes_read - mip_size - mip_size * frame);
data += vtf.file.get_buffer(mip_size);

bytes_read += mip_size + mip_size * (vtf.frames - 1);

var img := Image.create_from_data(vtf.width, vtf.height, use_mipmaps, format, data);
if not img: return null;

if srgb_conversion_method == vtf.SRGBConversionMethod.DURING_IMPORT:
img.decompress();
img.compress(Image.COMPRESS_S3TC);

vtf.alpha = vtf.flags & vtf.Flags.TEXTUREFLAGS_ONEBITALPHA or vtf.flags & vtf.Flags.TEXTUREFLAGS_EIGHTBITALPHA;

return ImageTexture.create_from_image(img);
6 changes: 6 additions & 0 deletions addons/godotvmf/godotvmt/formats/vtf_frame_reader.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class_name VTFFrameReader extends RefCounted


## Base class for reading frames from a VTF file. Each image format should have its own implementation of this class.
static func read(vtf: VTFLoader, frame: int, srgb_conversion_method: VTFLoader.SRGBConversionMethod) -> ImageTexture:
return null;
27 changes: 27 additions & 0 deletions addons/godotvmf/godotvmt/formats/vtf_i8.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class_name VTFI8 extends VTFFrameReader

static func read(vtf: VTFLoader, frame: int, srgb_conversion_method: VTFLoader.SRGBConversionMethod) -> ImageTexture:
var data := PackedByteArray();
var bytes_read := 0;
var use_mipmaps := not (vtf.flags & vtf.Flags.TEXTUREFLAGS_NOMIP);
var multiplier := 1 if vtf.hires_image_format == vtf.ImageFormat.I8 else 2;
var output_format := Image.FORMAT_L8 if vtf.hires_image_format == vtf.ImageFormat.I8 else Image.FORMAT_LA8;

frame = vtf.frames - 1 - frame;

for i in range(vtf.mipmap_count):
var mip_width = max(1, vtf.width >> i);
var mip_height = max(1, vtf.height >> i);
var mip_size = mip_width * mip_height * multiplier; # I8 has 1 byte per pixel

vtf.file.seek(vtf.file.get_length() - bytes_read - mip_size - mip_size * frame);
var chunk := vtf.file.get_buffer(mip_size);

data += chunk;
bytes_read += mip_size + mip_size * (vtf.frames - 1);

var img := Image.create_from_data(vtf.width, vtf.height, use_mipmaps, output_format, data);

if not img: return null;

return ImageTexture.create_from_image(img);
36 changes: 36 additions & 0 deletions addons/godotvmf/godotvmt/formats/vtf_rgba.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
class_name VTFRGBA extends VTFFrameReader

static func read(vtf: VTFLoader, frame: int, srgb_conversion_method: VTFLoader.SRGBConversionMethod) -> ImageTexture:
var data := PackedByteArray();
var bytes_read := 0;
var use_mipmaps := not (vtf.flags & vtf.Flags.TEXTUREFLAGS_NOMIP);
var is_abgr := vtf.hires_image_format == vtf.ImageFormat.ABGR8888 || vtf.hires_image_format == vtf.ImageFormat.BGR888;
var is_no_alpha := vtf.hires_image_format >= vtf.ImageFormat.RGB888;
var channels_count := 3 if is_no_alpha else 4;
var output_format := Image.FORMAT_RGB8 if is_no_alpha else Image.FORMAT_RGBA8;


frame = vtf.frames - 1 - frame;

for i in range(vtf.mipmap_count):
var mip_width = max(1, vtf.width >> i);
var mip_height = max(1, vtf.height >> i);
var mip_size = mip_width * mip_height * channels_count; # RGBA8888 has 4 bytes per pixel

vtf.file.seek(vtf.file.get_length() - bytes_read - mip_size - mip_size * frame);
var chunk := vtf.file.get_buffer(mip_size);

if is_abgr: chunk.reverse();

data += chunk;
bytes_read += mip_size + mip_size * (vtf.frames - 1);

var img := Image.create_from_data(vtf.width, vtf.height, use_mipmaps, output_format, data);

if is_abgr:
img.flip_x();
img.flip_y();

if not img: return null;

return ImageTexture.create_from_image(img);
35 changes: 17 additions & 18 deletions addons/godotvmf/godotvmt/vmt_loader.gd
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,14 @@ static func parse_transform(transform_data: String):
translate = translate,
}

static func get_material_load_path(material: String) -> String:
var path_root = normalize_path(VMFConfig.materials.target_folder + "/" + material).to_lower();

if ResourceLoader.exists(path_root + ".tres"): return path_root + ".tres";
if ResourceLoader.exists(path_root + ".res"): return path_root + ".res";
if ResourceLoader.exists(path_root + ".vmt"): return path_root + ".vmt";
return "";

static func load(path: String):
var structure = VDFParser.parse(path, true);

Expand Down Expand Up @@ -75,8 +83,8 @@ static func load(path: String):
key = key.replace('$', '').replace('%', '');

if is_compile_key and value and key != "keywords":
var compile_keys = material.get_meta("compile_keys", []);
compile_keys.append(key);
var compile_keys := material.get_meta("compile_keys", []) as Array;
compile_keys.append(key.to_lower());
material.set_meta("compile_keys", compile_keys);

if material is ShaderMaterial && not is_blend_texture:
Expand Down Expand Up @@ -106,27 +114,18 @@ static func normalize_path(path: String) -> String:
return path.replace('\\', '/').replace('//', '/').replace('res:/', 'res://');

static func has_material(material: String) -> bool:
var material_path = normalize_path(VMFConfig.materials.target_folder + "/" + material + ".tres").to_lower();

if not ResourceLoader.exists(material_path):
material_path = material_path.replace(".tres", ".vmt");

if not ResourceLoader.exists(material_path):
return false;
return get_material_load_path(material) != "";

return true;
static func is_material_ignored(material_name: String) -> bool:
return VMFConfig.materials.ignore.any(func(rx: String) -> bool: return material_name.match(rx.to_lower()));

static func get_material(material: String) -> Material:
var cached_material = VMFCache.get_cached(material);
if cached_material:
return cached_material as Material;

var material_path = normalize_path(VMFConfig.materials.target_folder + "/" + material + ".tres").to_lower();

if not ResourceLoader.exists(material_path):
material_path = material_path.replace(".tres", ".vmt");

if not ResourceLoader.exists(material_path):
var material_path = get_material_load_path(material);
if material_path == "":
if not VMFCache.is_file_logged(material_path):
VMFLogger.warn("Material not found: " + material_path);
VMFCache.add_logged_file(material_path);
Expand Down Expand Up @@ -161,8 +160,8 @@ static func get_texture_size(side_material: String) -> Vector2:

if not texture and (material is ShaderMaterial):
texture = material.get_shader_parameter('basetexture');
if not texture:

if not texture:
cached_value = Vector2(default_texture_size, default_texture_size);
VMFCache.add_cached(cache_key, cached_value);
return cached_value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func create_blend_material(paths: PackedStringArray):
var path1 = (vmts[0] as String).get_base_dir()
var path2 = (vmts[1] as String).get_base_dir()

var save_path = path1.get_base_dir() + '/' + blend_file_name;
var save_path = path1 + '/' + blend_file_name;
print("Saving blended VMT to: " + save_path);

var file := FileAccess.open(save_path, FileAccess.WRITE);
Expand Down
25 changes: 18 additions & 7 deletions addons/godotvmf/godotvmt/vmt_transformer.gd
Original file line number Diff line number Diff line change
Expand Up @@ -121,18 +121,29 @@ func detailblendmode(material: Material, value: Variant):
if "detail_blend_mode" in material:
material.set("detail_blend_mode", value);

func color2(material: Material, value: Variant):
if material is not BaseMaterial3D: return;
var color_str = str(value).replace("{", "").replace("}", "").strip_edges();
var parts = color_str.split(" ", false);
if parts.size() >= 3:
material.albedo_color = Color(
parts[0].to_float() / 255.0,
parts[1].to_float() / 255.0,
parts[2].to_float() / 255.0
);

func surfaceprop(material: Material, value: Variant):
material.set_meta("surfaceprop", value);

func basetexturetransform(material: Material, value: Variant):
if "uv1_scale" not in material:
return;
if "uv1_offset" not in material:
return;

var transform = VMTLoader.parse_transform(value);
material.uv1_scale = Vector3(transform.scale.x, transform.scale.y, 1);
material.uv1_offset = Vector3(transform.translate.x, transform.translate.y, 0);

if material is ShaderMaterial:
material.set_shader_parameter("uv1_scale", Vector3(transform.scale.x, transform.scale.y, 1));
material.set_shader_parameter("uv1_offset", Vector3(transform.translate.x, transform.translate.y, 0));
elif "uv1_scale" in material and "uv1_offset" in material:
material.uv1_scale = Vector3(transform.scale.x, transform.scale.y, 1);
material.uv1_offset = Vector3(transform.translate.x, transform.translate.y, 0);

func basetexturetransform2(material: Material, value: Variant):
if "uv1_scale2" not in material:
Expand Down
Loading