コード例 #1
0
ファイル: scene.cpp プロジェクト: tapio/weep
Entity SceneLoader::instantiate(Json def, Resources& resources, const string& pathContext)
{
	ASSERT(def.is_object());

	if (def["prefab"].is_string()) {
		const string& prefabName = def["prefab"].string_value();
		auto prefabIter = prefabs.find(prefabName);
		if (prefabIter != prefabs.end()) {
			def = assign(prefabIter->second, def);
		} else if (endsWith(prefabName, ".json")) {
				string err;
				Json extPrefab = Json::parse(resources.getText(resolvePath(pathContext, prefabName), Resources::NO_CACHE), err);
				if (!err.empty()) {
					logError("Failed to parse prefab \"%s\": %s", prefabName.c_str(), err.c_str());
				} else {
					def = assign(extPrefab, def);
					prefabs[prefabName] = extPrefab;
				}
		} else {
			logError("Could not find prefab \"%s\"", prefabName.c_str());
		}
	}

	Entity entity = world->create();

	if (def["name"].is_string()) {
		entity.tag(def["name"].string_value());
#ifdef USE_DEBUG_NAMES
		DebugInfo info;
		info.name = def["name"].string_value();
		entity.add<DebugInfo>(info);
	} else {
		static uint debugId = 0;
		DebugInfo info;
		if (def["prefab"].is_string())
			info.name = def["prefab"].string_value() + "#";
		else if (def["geometry"].is_string())
			info.name = def["geometry"].string_value() + "#";
		else info.name = "object#";
		info.name += std::to_string(debugId++);
		entity.tag(info.name);
		entity.add<DebugInfo>(info);
#endif
	}

	// Parse transform
	if (!def["position"].is_null() || !def["rotation"].is_null() || !def["scale"].is_null() || !def["geometry"].is_null()) {
		Transform transform;
		setVec3(transform.position, def["position"]);
		setVec3(transform.scale, def["scale"]);
		if (!def["rotation"].is_null()) {
			const Json& rot = def["rotation"];
			if (rot.is_array() && rot.array_items().size() == 4)
				transform.rotation = quat(rot[3].number_value(), rot[0].number_value(), rot[1].number_value(), rot[2].number_value());
			else transform.rotation = quat(toVec3(rot));
		}
		entity.add(transform);
	}

	// Parse light
	const Json& lightDef = def["light"];
	if (!lightDef.is_null()) {
		Light light;
		const string& lightType = lightDef["type"].string_value();
		if (lightType == "ambient") light.type = Light::AMBIENT_LIGHT;
		else if (lightType == "point") light.type = Light::POINT_LIGHT;
		else if (lightType == "directional") light.type = Light::DIRECTIONAL_LIGHT;
		else if (lightType == "spot") light.type = Light::SPOT_LIGHT;
		else if (lightType == "area") light.type = Light::AREA_LIGHT;
		else if (lightType == "hemisphere") light.type = Light::HEMISPHERE_LIGHT;
		else logError("Unknown light type \"%s\"", lightType.c_str());
		setColor(light.color, lightDef["color"]);
		if (!def["position"].is_null())
			light.position = toVec3(def["position"]);
		if (!lightDef["direction"].is_null())
			light.direction = toVec3(lightDef["direction"]);
		setNumber(light.distance, lightDef["distance"]);
		setNumber(light.decay, lightDef["decay"]);
		entity.add(light);
		numLights++;
	}

	if (!def["geometry"].is_null()) {
		Model model;
		parseModel(model, def, resources, pathContext);
		entity.add(model);
		numModels++;
	}

	// Patch bounding box
	// TODO: Bounding box is not correct if scale changed at runtime
	if (entity.has<Model>() && entity.has<Transform>()) {
		Model& model = entity.get<Model>();
		const Transform& trans = entity.get<Transform>();
		model.bounds.min = model.lods[0].geometry->bounds.min * trans.scale;
		model.bounds.max = model.lods[0].geometry->bounds.max * trans.scale;
		model.bounds.radius = model.lods[0].geometry->bounds.radius * glm::compMax(trans.scale);
	}

	// Parse body (needs to be after geometry, transform, bounds...)
	if (!def["body"].is_null()) {
		const Json& bodyDef = def["body"];
		ASSERT(bodyDef.is_object());
		ASSERT(entity.has<Model>());
		ASSERT(entity.has<Transform>());
		const Model& model = entity.get<Model>();
		const Transform& transform = entity.get<Transform>();

		float mass = 0.f;
		setNumber(mass, bodyDef["mass"]);

		btCollisionShape* shape = NULL;
		const string& shapeStr = bodyDef["shape"].string_value();
		vec3 extents = model.bounds.max - model.bounds.min;
		if (shapeStr == "box") {
			shape = new btBoxShape(convert(extents * 0.5f));
		} else if (shapeStr == "sphere") {
			shape = new btSphereShape(model.bounds.radius);
		} else if (shapeStr == "cylinder") {
			shape = new btCylinderShape(convert(extents * 0.5f));
		} else if (shapeStr == "capsule") {
			float r = glm::max(extents.x, extents.z) * 0.5f;
			shape = new btCapsuleShape(r, extents.y);
		} else if (shapeStr == "trimesh") {
			Geometry* colGeo = nullptr;
			if (bodyDef["geometry"].is_string()) {
				colGeo = resources.getGeometry(bodyDef["geometry"].string_value());
			} else {
				if (bodyDef["geometry"].is_array())
					logError("LODs not supported for collision mesh.");
				colGeo = model.lods[0].geometry;
			}
			if (!colGeo->collisionMesh)
				colGeo->generateCollisionTriMesh();
			if (mass <= 0.f) { // Static mesh
				shape = new btBvhTriangleMeshShape(colGeo->collisionMesh, true);
			} else {
				shape = new btGImpactMeshShape(colGeo->collisionMesh);
				static_cast<btGImpactMeshShape*>(shape)->updateBound();
			}
		} else {
			logError("Unknown shape %s", shapeStr.c_str());
		}
		ASSERT((shapeStr == "trimesh" || bodyDef["geometry"].is_null()) && "Trimesh shape type required if body.geometry is specified");
		ASSERT(shape);

		btVector3 inertia(0, 0, 0);
		shape->calculateLocalInertia(mass, inertia);

		btRigidBody::btRigidBodyConstructionInfo info(mass, NULL, shape, inertia);
		info.m_startWorldTransform = btTransform(convert(transform.rotation), convert(transform.position));
		setNumber(info.m_friction, bodyDef["friction"]);
		setNumber(info.m_rollingFriction, bodyDef["rollingFriction"]);
		setNumber(info.m_restitution, bodyDef["restitution"]);
		if (bodyDef["noSleep"].bool_value()) {
			info.m_linearSleepingThreshold = 0.f;
			info.m_angularSleepingThreshold = 0.f;
		}
		entity.add<btRigidBody>(info);
		numBodies++;
		btRigidBody& body = entity.get<btRigidBody>();
		if (!bodyDef["angularFactor"].is_null())
			body.setAngularFactor(convert(toVec3(bodyDef["angularFactor"])));
		if (!bodyDef["linearFactor"].is_null())
			body.setLinearFactor(convert(toVec3(bodyDef["linearFactor"])));
		if (bodyDef["noGravity"].bool_value())
			body.setFlags(body.getFlags() | BT_DISABLE_WORLD_GRAVITY);
		body.setUserIndex(entity.get_id());
		if (world->has_system<PhysicsSystem>())
			world->get_system<PhysicsSystem>().add(entity);
	}

	if (!def["animation"].is_null()) {
		ASSERT(entity.has<Model>());
		const Json& animDef = def["animation"];
		BoneAnimation anim;
		setNumber(anim.speed, animDef["speed"]);
		entity.add(anim);
		if (animDef["play"].is_bool() && animDef["play"].bool_value())
			world->get_system<AnimationSystem>().play(entity);
		else world->get_system<AnimationSystem>().stop(entity);
	}

	if (def["trackGround"].bool_value()) {
		ASSERT(entity.has<btRigidBody>());
		entity.add<GroundTracker>();
	}

	if (def["trackContacts"].bool_value()) {
		ASSERT(entity.has<btRigidBody>());
		entity.add<ContactTracker>();
	}

	if (def["triggerVolume"].is_object()) {
		ASSERT(entity.has<Transform>());
		const Json& triggerDef = def["triggerVolume"];
		TriggerVolume& trigger = entity.add<TriggerVolume>();

		setNumber(trigger.times, triggerDef["times"]);
		setNumber(trigger.bounds.radius, triggerDef["radius"]);
		setVec3(trigger.bounds.min, triggerDef["min"]);
		setVec3(trigger.bounds.min, triggerDef["max"]);

		if (triggerDef["receiver"].is_string())
			trigger.receiverModule = id::hash(triggerDef["receiver"].string_value());
		if (triggerDef["enterMessage"].is_string())
			trigger.enterMessage = id::hash(triggerDef["enterMessage"].string_value());
		else if (triggerDef["enterMessage"].is_number())
			trigger.enterMessage = triggerDef["enterMessage"].number_value();
		if (triggerDef["exitMessage"].is_string())
			trigger.exitMessage = id::hash(triggerDef["exitMessage"].string_value());
		else if (triggerDef["exitMessage"].is_number())
			trigger.exitMessage = triggerDef["exitMessage"].number_value();

		if (triggerDef["groups"].is_number())
			trigger.groups = 1 << (uint)triggerDef["groups"].number_value();
		else if (triggerDef["groups"].is_array()) {
			for (const auto& item : triggerDef["groups"].array_items())
				trigger.groups |= 1 << (uint)item.number_value();
		}
	}

	if (def["triggerGroup"].is_number()) {
		ASSERT(entity.has<Transform>());
		entity.add<TriggerGroup>().group = 1 << (uint)def["triggerGroup"].number_value();
	}

	if (!def["moveSound"].is_null()) {
		const Json& soundDef = def["moveSound"];
		MoveSound sound;
		if (soundDef["event"].is_string())
			sound.event = id::hash(soundDef["event"].string_value());
		setNumber(sound.stepLength, soundDef["step"]);
		ASSERT(sound.event);
		ASSERT(entity.has<Transform>());
		sound.prevPos = entity.get<Transform>().position;
		entity.add(sound);
	}

	if (!def["contactSound"].is_null()) {
		const Json& soundDef = def["contactSound"];
		ContactSound sound;
		if (soundDef["event"].is_string())
			sound.event = id::hash(soundDef["event"].string_value());
		ASSERT(sound.event);
		ASSERT(entity.has<Transform>());
		ASSERT(entity.has<ContactTracker>());
		entity.add(sound);
	}

	return entity;
}