--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,3 @@
-# set up cmake to download the external projects
-include(FetchContent)
 cmake_policy(SET CMP0054 NEW)
 cmake_minimum_required(VERSION 3.18)
 
@@ -24,59 +22,54 @@
 file(GLOB_RECURSE web-ifc-source web-ifc/*.cpp)
 file(GLOB_RECURSE web-ifc-wasm wasm/*.cpp)
 
-# download the external projects and save their paths
-Message("Downloading FastFloat")
-FetchContent_Declare(fastfloat GIT_REPOSITORY "https://github.com/fastfloat/fast_float" GIT_TAG "2b2395f9ac836ffca6404424bcc252bff7aa80e4" SOURCE_SUBDIR "../")
-FetchContent_MakeAvailable(fastfloat)
-FetchContent_GetProperties(fastfloat)
-
-Message("Downloading TinyNURBS")
-FetchContent_Declare(tinynurbs GIT_REPOSITORY "https://github.com/QuimMoya/tinynurbs" GIT_TAG "47115cd9b6e922b27bbc4ab01fdeac2e9ea597a4" SOURCE_SUBDIR "../")
-FetchContent_MakeAvailable(tinynurbs)
-FetchContent_GetProperties(tinynurbs)
-
-Message("Downloading TinyCPPTest")
-FetchContent_Declare(tinycpptest GIT_REPOSITORY "https://github.com/kovacsv/TinyCppTest" GIT_TAG "12e42c8ac6e032ce450fb3f772ebdfd1ddc6008c" SOURCE_SUBDIR "../")
-FetchContent_MakeAvailable(tinycpptest)
-FetchContent_GetProperties(tinycpptest)
-
-Message("Downloading GLM")
-FetchContent_Declare(glm GIT_REPOSITORY "https://github.com/g-truc/glm" GIT_TAG "8d1fd52e5ab5590e2c81768ace50c72bae28f2ed" SOURCE_SUBDIR "../")
-FetchContent_MakeAvailable(glm)
-FetchContent_GetProperties(glm)
-
-Message("Downloading Earcut")
-FetchContent_Declare(earcut GIT_REPOSITORY "https://github.com/mapbox/earcut.hpp" GIT_TAG "4811a2b69b91f6127a75e780de6e2113609ddabb" SOURCE_SUBDIR "../")
-FetchContent_MakeAvailable(earcut)
-FetchContent_GetProperties(earcut)
-
-Message("Downloading CDT")
-FetchContent_Declare(cdt GIT_REPOSITORY "https://github.com/artem-ogre/CDT" GIT_TAG "4d0c9026b8ec846fe544897e7111f8f9080d5f8a" SOURCE_SUBDIR "../")
-FetchContent_MakeAvailable(cdt)
-FetchContent_GetProperties(cdt)
-
-Message("Downloading spdlog")
-FetchContent_Declare(spdlog GIT_REPOSITORY "https://github.com/gabime/spdlog" GIT_TAG "f355b3d58f7067eee1706ff3c801c2361011f3d5" SOURCE_SUBDIR "../")
-FetchContent_MakeAvailable(spdlog)
-FetchContent_GetProperties(spdlog)
-
-Message("Downloading StdUUID")
-FetchContent_Declare(stduuid GIT_REPOSITORY "https://github.com/mariusbancila/stduuid" GIT_TAG "3afe7193facd5d674de709fccc44d5055e144d7a" EXCLUDE_FROM_ALL)
-FetchContent_MakeAvailable(stduuid)
-FetchContent_GetProperties(stduuid)
+find_package(glm REQUIRED)
+find_package(spdlog REQUIRED)
+find_package(FastFloat CONFIG REQUIRED)
+
+set(tinynurbs_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/tinynurbs")
+set(earcut_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/earcut.hpp")
+set(cdt_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/CDT")
+set(stduuid_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/stduuid")
+set(unordered_dense_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/unordered_dense")
 
-
-Message("Downloads Finished")
+if(BUILD_TESTING)
+	set(tinycpptest_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/TinyCppTest")
+endif()
 
 function(PARAM_SETTER THE_EXECUTABLE)
 	target_include_directories(${THE_EXECUTABLE} PUBLIC $<BUILD_INTERFACE:${tinynurbs_SOURCE_DIR}/include>)
-	target_include_directories(${THE_EXECUTABLE} PUBLIC $<BUILD_INTERFACE:${fastfloat_SOURCE_DIR}/include>)
 	target_include_directories(${THE_EXECUTABLE} PUBLIC $<BUILD_INTERFACE:${cdt_SOURCE_DIR}/CDT/include>)
-	target_include_directories(${THE_EXECUTABLE} PUBLIC $<BUILD_INTERFACE:${glm_SOURCE_DIR}/>)
-	target_include_directories(${THE_EXECUTABLE} PUBLIC $<BUILD_INTERFACE:${glm_SOURCE_DIR}/glm>)
 	target_include_directories(${THE_EXECUTABLE} PUBLIC $<BUILD_INTERFACE:${earcut_SOURCE_DIR}/include>)
-	target_include_directories(${THE_EXECUTABLE} PUBLIC $<BUILD_INTERFACE:${spdlog_SOURCE_DIR}/include>)
 	target_include_directories(${THE_EXECUTABLE} PUBLIC $<BUILD_INTERFACE:${stduuid_SOURCE_DIR}/include>)
+	target_include_directories(${THE_EXECUTABLE} PUBLIC $<BUILD_INTERFACE:${unordered_dense_SOURCE_DIR}/include>)
+
+	foreach(dir IN ITEMS tinynurbs_SOURCE_DIR earcut_SOURCE_DIR cdt_SOURCE_DIR stduuid_SOURCE_DIR unordered_dense_SOURCE_DIR)
+		if(NOT EXISTS "${${dir}}")
+			message(FATAL_ERROR "Missing vendored dependency directory: ${dir}='${${dir}}'")
+		endif()
+	endforeach()
+
+	if(NOT TARGET glm::glm)
+		message(FATAL_ERROR "glm found but target glm::glm not provided")
+	endif()
+
+	target_link_libraries(${THE_EXECUTABLE} PUBLIC glm::glm)
+
+	if(TARGET spdlog::spdlog_header_only)
+		target_link_libraries(${THE_EXECUTABLE} PUBLIC spdlog::spdlog_header_only)
+	elseif(TARGET spdlog::spdlog)
+		target_link_libraries(${THE_EXECUTABLE} PUBLIC spdlog::spdlog)
+	else()
+		message(FATAL_ERROR "spdlog found but no known target (spdlog::spdlog_header_only / spdlog::spdlog)")
+	endif()
+
+	if(TARGET FastFloat::fast_float)
+		target_link_libraries(${THE_EXECUTABLE} PUBLIC FastFloat::fast_float)
+	elseif(TARGET fast_float::fast_float)
+		target_link_libraries(${THE_EXECUTABLE} PUBLIC fast_float::fast_float)
+	else()
+		message(FATAL_ERROR "FastFloat CMake package found, but no known target (FastFloat::fast_float / fast_float::fast_float)")
+	endif()
 
 	if(NOT MSVC)
 		target_compile_options(${THE_EXECUTABLE} PUBLIC "-Wall")
@@ -133,17 +126,19 @@
 	)
 
 	# build parameters for web-ifc-test
-	add_executable(web-ifc-test ${web-ifc-source} "./test/encoding_test.cpp" "./test/main.cpp" "./test/io_helpers.cpp")
-	param_setter(web-ifc-test)
-	target_include_directories(web-ifc-test PUBLIC ${tinycpptest_SOURCE_DIR}/Sources)
-
-	add_test(web-ifc-test web-ifc-test)
-	set_tests_properties(web-ifc-test PROPERTIES LABELS "web-ifc")
-
-	# build parameters for web-ifc in testing environment
-	add_executable(web-ifc ${web-ifc-source} "./test/web-ifc-test.cpp" "./test/io_helpers.cpp")
-	param_setter(web-ifc)
-	target_include_directories(web-ifc PUBLIC ${tinycpptest_SOURCE_DIR}/Sources)
+	if(BUILD_TESTING)
+		add_executable(web-ifc-test ${web-ifc-source} "./test/encoding_test.cpp" "./test/main.cpp" "./test/io_helpers.cpp")
+		param_setter(web-ifc-test)
+		target_include_directories(web-ifc-test PUBLIC ${tinycpptest_SOURCE_DIR}/Sources)
+
+		add_test(web-ifc-test web-ifc-test)
+		set_tests_properties(web-ifc-test PROPERTIES LABELS "web-ifc")
+
+		# build parameters for web-ifc in testing environment
+		add_executable(web-ifc ${web-ifc-source} "./test/web-ifc-test.cpp" "./test/io_helpers.cpp")
+		param_setter(web-ifc)
+		target_include_directories(web-ifc PUBLIC ${tinycpptest_SOURCE_DIR}/Sources)
+	endif()
 
 	# comment these to prevent debug files being generated
 	if(NOT RELEASE)
@@ -170,29 +165,6 @@
 		DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/web-ifc
 	)
 
-	# Install bundled dependency headers (all header-only)
-	install(DIRECTORY ${fastfloat_SOURCE_DIR}/include/
-		DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
-	)
-	install(DIRECTORY ${tinynurbs_SOURCE_DIR}/include/
-		DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
-	)
-	install(DIRECTORY ${glm_SOURCE_DIR}/glm
-		DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
-	)
-	install(DIRECTORY ${earcut_SOURCE_DIR}/include/
-		DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
-	)
-	install(DIRECTORY ${cdt_SOURCE_DIR}/CDT/include/
-		DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
-	)
-	install(DIRECTORY ${spdlog_SOURCE_DIR}/include/
-		DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
-	)
-	install(DIRECTORY ${stduuid_SOURCE_DIR}/include/
-		DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
-	)
-
 	# Export targets with namespace
 	install(EXPORT web-ifc-targets
 		FILE web-ifc-targets.cmake
